A lightweight TypesScript REST API framework built on top of Restana
This 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.
According to Restana's docs and @jkyberneees's research and benchmarking, Restana is one of the fastest HTTP server's written in Node.js.
As you can see here, 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.
This package is currently hosted on Github's Package Registry so you will need to do some stuff
TODO: DO STUFF
Then install package via npm or yarn
npm install @colevoss/servana --save
# OR
yarn add @colevoss/servana
This example sets up an HTTP server with a route of GET /my-route/hello
that will respond with a JSON object of { howdy: 'hello ' }
import { Service, Server, Get, Context } from '@colevoss/servana';
// Create a route and its actions
class MyService extends Service {
// Base Route
public route = 'my-route';
@Get('/hello') // Creates the route GET /my-route/hello
public hello(context: Context) {
// Sends back JSON
context.send({ howdy: 'Hello' });
}
}
// Create the server
class MyServer extends Server {
public name = 'MyServer';
}
// Start it up (async/await)
const main = async () => {
// Initialize the server and service routes
const server = await MyServer.create([MyService]);
// Start the service and listen on route 3000
await server.start(3000);
};
main();
// OR Start up using promises
MyServer.create([MyService]).then((server) => {
server.start(3000);
});
The @Get
, @Post
, @Put
, and @Delete
decorators are available to create your service endpoints. Supply a path to each decorator to create that endpoint.
class TestService extends Service {
public route = 'test';
@Get('/get-route')
public async getRoute(ctx: Context) {
/* ... */
}
@Post('/post-route')
public async postRoute(ctx: Context) {
/* ... */
}
@Put('/put-route')
public async putRoute(ctx: Context) {
/* ... */
}
@Delete('/delete-route')
public async deleteRoute(ctx: Context) {
/* ... */
}
}
Since restana uses find-my-way
for its routing, the Route Decorators support these path formats.
You can use path parameters like /test/:id
and the id
parameter will be available at context.params.id
that is passed to that endpoint.
note: More methods will probably added later.
The Servce
class is used to create a collection of routes off of a base route. Extend this class to create new services and endpoints.
public route: string;
The route
property acts as a base route for all endpoints declared in that service.
note: You do not need to add a leading /
to the route property.
public contextFactory<T>(
request: Request,
response: Response
): T extends Context`
The 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.
new Context(request: Request, response: Response)
public params: { [key: string]: string }
The route params for the endpoint's route. A route of /:id
will provide the params at context.params.id
The body
property is populated by the request body provided on post, put, and delete requests
interface TestBody {
abra: string;
ala: string;
}
class BodyExampleService extends Service {
public route = 'hello';
@Get('/test')
public bodyExamplePath(ctx: Context<TestBody>) {
// Access the request body at ctx.body
}
}
fetch('host/hello/test', {
method: 'POST',
body: JSON.stringify({
abra: 'kadabra',
ala: 'kazam',
}),
});
public query: Q;
The query property is populated by the query parameters provided when a request to that endpoint is requested.
interface TestQuery {
abra: string;
ala: string;
}
class QueryExampleService extends Service {
public route = 'hello';
@Get('/test')
public queryExamplePath(ctx: Context<{}, TestQuery>) {
// Access the query params at ctx.query
}
}
A request to GET hello/test?abra=kadabra&ala=kazam
will populate the ctx.query
object as follows:
{
abra: 'kadabra',
ala: 'kazam'
}
public send<T>(data: T, code: number = 200)
Use 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
.
public error<E>(error: E)
Servana uses 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:
import { InternalServerError } from '@colevoss/servana';