Node Service API Router
This is the entry point of your Service API when you decide to use this library.
Installation
With NPM:
syntax:bash
npm install @crystallize/node-service-api-router
With Yarn:
syntax:bash
yarn add @crystallize/node-service-api-router
Usage
Here is what a valid index.ts would look like with only one endpoint.
syntax:typescript
import {
createServiceApiApp,
ValidatingRequestRouting,
StandardRouting,
authenticatedMiddleware,
} from '@crystallize/node-service-api-router';
import Koa from 'koa';
const routes: StandardRouting = {
'/': {
get: {
handler: async (ctx: Koa.Context) => {
ctx.body = { msg: `Crystallize Service API - Tenant ${process.env.CRYSTALLIZE_TENANT_IDENTIFIER}` };
},
},
},
};
const bodyConvertedRoutes: ValidatingRequestRouting = {};
const { run } = createServiceApiApp(bodyConvertedRoutes, routes, authenticatedMiddleware(`${process.env.JWT_SECRET}`));
run(process.env.PORT ? parseInt(process.env.PORT) : 3000);
This is using Koa JS for the Middleware management.
Note: CORS is managed in this library.
StandardRouting
Standard routes are standard by definition, nothing specific to understand. You get access to the request (and more) through the Koa.Context and you need to set the Body. (alias of ctx.response.body)
ValidatingRequestRouting
This is where this library takes all its meaning. It does not provide any features by itself, but it enables a concept.
syntax:typescript
const bodyConvertedRoutes: ValidatingRequestRouting = {
'/my/endpoint': {
post: {
schema: requestInputSchema,
handler: requestInputHandler,
args: (context: any) => {};
}
}
}
It means on the '/my/endpoint' endpoint, the API Router will intercept the request, check and validate the entry against requestInputSchema, run the args function to get handler’s arguments to finally execute the handler when it validates.
But the response is not returned yet, so you can still extend it in the standard routes:
syntax:typescript
const routes: StandardRouting = {
'/cart': {
post: {
handler: async (ctx: Koa.Context) => {
const cart = ctx.body as Cart;
ctx.body = {
...cart,
hello: 'world',
};
},
},
},
};
const bodyConvertedRoutes: ValidatingRequestRouting = {
'/cart': {
post: {
schema: cartPayload,
handler: handleCartRequestPayload,
args: (context: Koa.Context): any => {},
},
},
};
With this code, if the Request validates against cartRequest, the body of the request will be converted to a Cart. And the standard route can still do whatever it wants, knowing the ctx.body is a Cart. In this example, we add a property to the response.
Why
This enables the next library: Node Service API Request Handlers which provides many highly customizable schemas and handlers (like the Cart mentioned above) so you can get started in minutes and you can extend them to make your Service API yours!
Authentification
Library comes with a default Authentication middleware. For each route that you describe, you have a boolean attribute named authenticated which is by default set to false.
syntax:typescript
'/echo': {
post: {
handler: async (ctx: Koa.Context) => {
ctx.body = {
echoed: ctx.request.body
}
}
}
},
'/authenticated/echo': {
post: {
authenticated: true,
handler: async (ctx: Koa.Context) => {
ctx.body = {
echoed: ctx.request.body,
user: ctx.user
}
}
}
},
In this example /authenticated/echo will be accessible with a valid authentication.
How does that work?
First, the middleware is passed to the router via Dependency Injection, therefore you can also provide your own.
syntax:javascript
const { run, router } = createServiceApiApp(
bodyConvertedRoutes,
routes,
authenticatedMiddleware(`${process.env.JWT_SECRET}`),
);
The authenticatedMiddleware provided by the library is simple:
- it will check the existence of a cookie name: jwt
- if that cookie exists, then it will decode it and check its signature. (That’s why you pass the JWT_SECRET)
- and it will check the sub property of the payload to be: isLoggedInOnServiceApiToken
Note: The last check is to be consistent with the provided MagickLink feature.
If one of the checks fails, the router will return a 401 error.
Once again, all is extendable. You may or may not use the authenticatedMiddleware and you can certainly pass your own to do your own logic.