An http/rest api server for deno. Based on std deno http library and use the concept of functional programming for your endpoint definitions.
import { Api, Route, EMethod } from "https://deno.land/x/deno_api_server/mod.ts";
const api = new Api({ port: 8080 });
api.addRoute(
new Route(EMethod.GET, '/')
.addPipe(({response}) => {
response.body = {message: 'Hello API'};
})
)
console.log(`Start server localhost:${api.serverConfig.port}`);
await api.listen();
/** INFO: don't forget route to your Api, see first example **/
new Route(EMethod.GET, '/say-hello')
.addPipe(({response}) => {
response.body = {message: 'Hello API'};
})
)
/** INFO: don't forget route to your Api, see first example **/
new Route([EMethod.GET, EMethod.POST], '/say-hello')
.addPipe(({response, request}) => {
response.body = { message: `Hello API by ${request.method}` };
});
/** INFO: don't forget route to your Api, see first example **/
new Route(EMethod.GET, '/state')
.addPipe(({response, state}) => {
const before = new Date();
response.body = {before};
state.set('called', before);
})
.addPipe(({response, state}) => {
Object.assign(response.body, {
passed: state.get('called'),
done: new Date()
})
});
/** INFO: don't forget route to your Api, see first example **/
new Route(EMethod.GET, '/error')
.addPipe(() => {
throw new RequestError('I dont like', 400);
})
new Route(
EMethod.GET,
new KeyMatch(
'/get-by-key-name/:id/:name',
{
id: { type: Number },
name: {}
}
)
)
.addPipe(({response, match}) => {
const { params } = match;
response.body = { message: `You call with keymatch`, id: params.get('id'), name: params.get('name') };
})
For more details about KeyMatch see API Documentation KeyMatch
Core server class, create an instance for your own api
Similar configuration of deno http service
new Api(config: IServerConfig)
Add new Routes / Entpoints
api.addRoute(route: IRoute)
Start server to listen
api.listen();
Core route or entpoint defintion for your api.
new Route(method: string[] | string, uri : IMatcher | string)
Add route pipeline
route.addPipe(pipe: IPipe) : Route
Example: Add a stack of pipes
route
.addPipe((context: IContext) => { ... })
.addPipe(async (context: IContext) => { ... })
Tip
use deconstruction for select context props
Execute a route pipeline, this will be called by api if route match with uri defintion
route.execute(url: URL, request: ServerRequest, response: IResponse): Promise<IContext>
Base endpoint matcher for simple definition of uris.
INFO
you can use a string for Route definition, the Route will create a simple UriMatch
More complex uri definition for your route with transpile your parameters. A KeyMatch can use a transform function to change the type of value
new KeyMatch(uri: string, describe: IKeyDescribes)
Examples:
new KeyMatch('/get/:id', { id: {} }) => match: /get/1, params: { id: '1' }
new KeyMatch('/get/:id', { id: {} }) => match: /get/hello-my, params: { id: 'hello-my' }
// with transpile type
new KeyMatch('/get/:id', { id: { type: Number } }) => match: /get/1, params: { id: 1 }
new KeyMatch('/get/:id', { id: { type: Number } }) => not match: /get/hello
'Any'
for any parameter like string, number and combinationNumber
for float and numbers'Int'
for integer numbers
import "https://deno.land/x/deno_api_server@v0.0.2/example.ts";
/**
* will start server on localhost:8080
* try all entpoints on example
**/
All presets defined in src/presets
All presets defined in src/presets
healthz
GET|HEAD /healthz for healthcheckstatus
GET /status similar to healthcheck but customizable definitions, see API DOC
Read body data of request.
Set state body
with string data
Set state bodyType
with the type of data == raw
import rawBodyPipe from 'https://deno.land/x/deno_api_server/src/presets/raw-body.pipe.ts';
route
.addPipe(rawBodyPipe)
.addPipe(({ state } : IContext )=> {
const anyBody = state.get('body');
});
Try to parse json data of request body. Otherwise throw 400 error.
Set state body
with json data
Set state bodyType
with the type of data == json
import jsonBodyPipe from 'https://deno.land/x/deno_api_server/src/presets/json-body.pipe.ts';
route
.addPipe(jsonBodyPipe)
.addPipe(({ state } : IContext )=> {
const validJsonBody = state.get('body');
});
Redirect pipe with default 302 redirection to your url. Basic implementation to redirect your route.
import redirectPipe from 'https://deno.land/x/deno_api_server/src/process/redirect.pipe.ts';
route
.addPipe(redirectPipe('/other-page'))
If you want to redirect a dynamic page use any capsule pipe function, like here
import redirectPipe from 'https://deno.land/x/deno_api_server/src/process/redirect.pipe.ts';
route
.addPipe((context: IContext) => {
const url = `/test-to-my?set=1`
return redirectPipe(url)(context);
})
Use buildin mocks to create a api request.
A simple example
import { mockApi } from 'https://deno.land/x/deno_api_server/dev_mod.ts';
Deno.test('Example mockApi route success request', async () => {
const route = new Route('GET', '/hello');
// create api
const api = mockApi(route);
await api.sendByArguments('GET', '/hello');
assertEquals(api.lastRoute === route, true);
assertEquals(api?.lastContext?.response.status, 200);
})
A payload example
import { mockApi, mockRequest } from 'https://deno.land/x/deno_api_server/dev_mod.ts';
Deno.test('Example mockApi post request with request data', async () => {
const route = new Route('POST', '/submit');
route
.addPipe(jsonBodyPipe)
.addPipe(({state, response}) => {
response.status = 201;
response.body = state.get('body');
});
const api = mockApi(route);
const request = mockRequest('POST', '/submit', {
name: 'super'
});
await api.sendByRequest(request);
assertEquals(api.lastRoute === route, true);
assertEquals(api?.lastContext?.response.status, 201);
assertEquals(api?.lastContext?.response.body, { name: 'super' });
});
More examples in example/unit-testing.test.ts
Tips and best practice to use this server.
- Write custom and small pipes to reuse
- create a custom createRoute function to setup your app
- use Route.di / Route.injections to share services and other stuff
- create factory functions for injections, like getConnection factory to connect to database only if route will execute
- write test by mock your injections (like mock service for your database connection)
- use state to pass data between your pipes
See example
folder for more use case examples.
route and api testing example and how to use build-in mock functions.
Use validasaur to validate your json body validasaur deno.land
Use djwt to handle and secure your endpoints, a very simple example to customize your process validasaur deno.land