{"id":23379036,"url":"https://github.com/colevoss/servana","last_synced_at":"2025-04-10T20:45:28.958Z","repository":{"id":35206524,"uuid":"215111040","full_name":"colevoss/servana","owner":"colevoss","description":"A lightweight TypesScript REST API framework built on top of","archived":false,"fork":false,"pushed_at":"2023-01-04T22:42:31.000Z","size":418,"stargazers_count":3,"open_issues_count":15,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-04-25T19:00:30.118Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/colevoss.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-10-14T17:57:45.000Z","updated_at":"2023-03-10T08:36:05.000Z","dependencies_parsed_at":"2023-01-15T16:09:56.593Z","dependency_job_id":null,"html_url":"https://github.com/colevoss/servana","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/colevoss%2Fservana","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/colevoss%2Fservana/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/colevoss%2Fservana/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/colevoss%2Fservana/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/colevoss","download_url":"https://codeload.github.com/colevoss/servana/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248294766,"owners_count":21079964,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-12-21T19:16:18.648Z","updated_at":"2025-04-10T20:45:28.941Z","avatar_url":"https://github.com/colevoss.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Servana\n\nA lightweight TypesScript REST API framework built on top of [Restana](https://github.com/jkyberneees/ana#readme)\n\n## What is this?\n\nThis library helps you write REST API's with TypeScript more easily and more enjoyably. Servana leverages concepts like class inheritance for `Service`'s and Decorators for actions. After working with custom TS wrappers for the Moleculer framework, I was motivated to port over some of the concepts I learned over to a REST API.\n\n## Why Restana?\n\nAccording to Restana's docs and @jkyberneees's research and benchmarking, Restana is one of the fastest HTTP server's written in Node.js.\n\nAs you can see [here](https://github.com/the-benchmarker/web-frameworks#full-table-1), Restana is much faster than other popular HTTP/REST node libraries like Express, Koa, and Fastify so I wanted to build my little TS framework on top of the fastest library out there.\n\n## Install\n\nThis package is currently hosted on Github's Package Registry so you will need to do some stuff\n\n```\nTODO: DO STUFF\n```\n\nThen install package via npm or yarn\n\n```bash\nnpm install @colevoss/servana --save\n\n# OR\n\nyarn add @colevoss/servana\n```\n\n## Examples\n\n### Super Simple\n\nThis example sets up an HTTP server with a route of `GET /my-route/hello` that will respond with a JSON object of `{ howdy: 'hello ' }`\n\nSee [`examples/super-simple.ts`](examples/super-simple.ts)\n\n```typescript\nimport { Service, Server, Get, Context } from '@colevoss/servana';\n\n// Create a route and its actions\nclass MyService extends Service {\n  // Base Route\n  public route = 'my-route';\n\n  @Get('/hello') // Creates the route GET /my-route/hello\n  public hello(context: Context) {\n    // Sends back JSON\n    context.send({ howdy: 'Hello' });\n  }\n}\n\n// Create the server\nclass MyServer extends Server {\n  public name = 'MyServer';\n}\n\n// Start it up (async/await)\nconst main = async () =\u003e {\n  // Initialize the server and service routes\n  const server = await MyServer.create([MyService]);\n\n  // Start the service and listen on route 3000\n  await server.start(3000);\n};\n\nmain();\n\n// OR Start up using promises\n\nMyServer.create([MyService]).then((server) =\u003e {\n  server.start(3000);\n});\n```\n\n## Spec\n\n### Route Decorators\n\nThe `@Get`, `@Post`, `@Put`, and `@Delete` decorators are available to create your service endpoints. Supply a path to each decorator to create that endpoint.\n\n```ts\nclass TestService extends Service {\n  public route = 'test';\n\n  @Get('/get-route')\n  public async getRoute(ctx: Context) {\n    /* ... */\n  }\n\n  @Post('/post-route')\n  public async postRoute(ctx: Context) {\n    /* ... */\n  }\n\n  @Put('/put-route')\n  public async putRoute(ctx: Context) {\n    /* ... */\n  }\n\n  @Delete('/delete-route')\n  public async deleteRoute(ctx: Context) {\n    /* ... */\n  }\n}\n```\n\nSince restana uses `find-my-way` for its routing, the Route Decorators support [these path formats](https://github.com/delvedor/find-my-way#supported-path-formats).\n\nYou can use path parameters like `/test/:id` and the `id` parameter will be available at `context.params.id` that is passed to that endpoint.\n\n**note:** More methods will probably added later.\n\n### [`Service`](src/Service.ts)\n\nThe `Servce` class is used to create a collection of routes off of a base route. Extend this class to create new services and endpoints.\n\n#### route\n\n```ts\npublic route: string;\n```\n\nThe `route` property acts as a base route for all endpoints declared in that service.\n**note**: You do not need to add a leading `/` to the route property.\n\n#### contextFactory\n\n```ts\npublic contextFactory\u003cT\u003e(\n  request: Request,\n  response: Response\n): T extends Context`\n```\n\nThe `contextFactory` function is called on each request and initializes an intance of Context to be passed to each endpoint by default. This function can be overwritten to supply a custom `Context` instance to each endpoint.\n\n### [`Context\u003cBody, Query\u003e`](src/Context.ts)\n\n#### Context\n\n```ts\nnew Context(request: Request, response: Response)\n```\n\n#### params\n\n```ts\npublic params: { [key: string]: string }\n```\n\nThe route params for the endpoint's route. A route of `/:id` will provide the params at `context.params.id`\n\n#### body\n\nThe `body` property is populated by the request body provided on post, put, and delete requests\n\n```ts\ninterface TestBody {\n  abra: string;\n  ala: string;\n}\n\nclass BodyExampleService extends Service {\n  public route = 'hello';\n\n  @Get('/test')\n  public bodyExamplePath(ctx: Context\u003cTestBody\u003e) {\n    // Access the request body at ctx.body\n  }\n}\n```\n\n```js\nfetch('host/hello/test', {\n  method: 'POST',\n  body: JSON.stringify({\n    abra: 'kadabra',\n    ala: 'kazam',\n  }),\n});\n```\n\n#### query\n\n```ts\npublic query: Q;\n```\n\nThe query property is populated by the query parameters provided when a request to that endpoint is requested.\n\n```ts\ninterface TestQuery {\n  abra: string;\n  ala: string;\n}\n\nclass QueryExampleService extends Service {\n  public route = 'hello';\n\n  @Get('/test')\n  public queryExamplePath(ctx: Context\u003c{}, TestQuery\u003e) {\n    // Access the query params at ctx.query\n  }\n}\n```\n\nA request to `GET hello/test?abra=kadabra\u0026ala=kazam` will populate the `ctx.query` object as follows:\n\n```ts\n{\n  abra: 'kadabra',\n  ala: 'kazam'\n}\n```\n\n#### send()\n\n```ts\npublic send\u003cT\u003e(data: T, code: number = 200)\n```\n\nUse the `send` function to respond with a JSON response. The object provided as `data` will be the response body when the endpoint is requested. You can optionally provide a different status code as the `code` to respond with a different status code. By default the status code is `200`.\n\n#### error()\n\n```ts\npublic error\u003cE\u003e(error: E)\n```\n\nServana uses [restify-errors](https://github.com/restify/errors) to provide a wide range of error objects that map to many HTTP status codes. Provide an instance of any restify error to this function and the request will be responded to appropriately. Errors can be imported directly from Servana like so:\n\n```ts\nimport { InternalServerError } from '@colevoss/servana';\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcolevoss%2Fservana","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcolevoss%2Fservana","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcolevoss%2Fservana/lists"}