A general purpose middleware, route modeling, and route matching framework.
- Familiar, express-like interface
- Nested routers and relative route matching
- Exact or inexact route matching
- Route parameter matching
- Optional routes or route params
- Extendable, integrate with any server framework like Lambda, express, etc.
- Comes with an implementation for routing in Lambda functions
There is no official documentation, but a good way to see example usage is the unit tests.
Instantiate a Router
import { Router } from '@tomkrcmar/lambda-router';
const router = new Router();
Simple HTTP request
// "get" can also be post, put, patch, delete
router.get('/hello', (req, res) => {
res.send('Hello, World!');
})
Route parameters
router.get('/thing/:id', (req, res) => {
const { id } = req.params;
res.send(`Thing data for thing number ${id}`);
})
Optional route parameters
router.get('/thing/:id?', (req, res) => {
const { id } = req.params;
if (id)
res.send(`Thing data for thing number ${id}`);
else
res.send('Thing index for all things');
})
// Regular text routes can also be optional:
router.get('path/to?/something?/maybe?', (req, res) => {
// This matches path, path/to, path/to/something, and path/to/something/maybe
});
Simple Middleware
router.use((req, res, ctx) => {
// All 3 arguments have their own `data` property for custom props passed down to subsequent middleware
req.data.myCustomValueA = 5;
res.data.myCustomValueB = 6;
ctx.data.myCustomValueC = 7;
// Allow subsequent middleware and route matching to continue
ctx.next();
// Note: ctx also contains other misc. information about the current router stack and dispatching state.
})
Nested Routers example
const thingRouter = new Router();
thingRouter.get('thing', (req, res) => {
res.send('Thing data');
})
router.use('/path/to/the', thingRouter);
// At this point, GET /path/to/the/thing will match the handler
Method chaining (set status code, set one header, set multiple headers, send with body)
router.get('/hello', (req, res) => {
res.status(200)
.header('Content-type', 'text/plain')
.setHeaders({
'X-Header-One': 'ABC',
'X-Header-Two': '123',
})
.send('Hello, World!');
})
CORS middlware example
import { Router } from '@tomkrcmar/lambda-router';
const router = new Router();
export default router;
router.use((req, res, ctx) => {
res.setHeaders({
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET,POST,PUT,PATCH,DELETE',
'Access-Control-Allow-Credentials': 'true',
'Access-Control-Allow-Headers': 'Content-Type,Authorization',
});
if (req.method === 'options')
res.status(200).send();
else
ctx.next();
})
Simply add your routes on a LambdaRouter
at the top level and supply it to your handler
export in lambda. The rest of your app can be built with Router
s and are completely portable, only the top-level router needs to be a LambdaRouter
.
There are multiple LambdaRouters available, but the default LambdaRouter
will attempt to detect the right version:
LambdaRouter
- Auto-detect API Gateway API versionApiGatewayLambdaRouter
- API Gateway V1 REST APIsApiGatewayV2LambdaRouter
- API Gateway V2 HTTP APIs
Minimal example
import { LambdaRouter } from '@tomkrcmar/lambda-router';
import myAppRouter from './my-app';
export function handler(event, context) {
return new LambdaRouter().use(myAppRouter).dispatchLambda(event, context);
}
Example of adding our CORS middleware above, and a hello world endpoint directly onto the top-level router:
import { LambdaRouter } from '@tomkrcmar/lambda-router';
import cors from './my-middleware/cors';
const router = new LambdaRouter();
router.use(cors);
router.get('/hello', (req, res) => {
res.send('Hello, World!');
})
export function handler(event, context) {
return router.dispatchLambda(event, context);
}
In order to integrate your route model into a server framework, there are three steps:
- Construct a
Request
- Call
Router.dispatch(request)
- Handle the
Response
The LambdaRouter
above performs all of these steps for you, constructing a Request
from your lambda event and context, and converting the Response
to the lambda handler response structure that lambda expects.
Minimal example of manually doing this with no conversion:
import { Request, Router } from '@tomkrcmar/lambda-router';
const router = new Router();
router.use('/hello', (req, res) => res.send('Hello, World!'))
const req = new Request();
req.path = '/hello';
const res = await router.dispatch(req);
// res.body should now be populated with 'Hello, World!' with status code 200 and other misc. fields present
# Install as a runtime dependency
npm install --save @tomkrcmar/lambda-router
# Install as a development dependency if you have your own build or bundling process
npm install --save-dev @tomkrcmar/lambda-router
Build the module yourself:
npm run build
# Build in watch mode:
npm run build:w
Run unit tests and coverage:
npm run test
# Test in watch mode:
npm run test:w