Skip to content

A general purpose middleware, route modeling, and route matching framework. Includes Lambda router implementation

Notifications You must be signed in to change notification settings

TomKrcmar/lambda-router

Repository files navigation

@tomkrcmar/lambda-router

npm

A general purpose middleware, route modeling, and route matching framework.

Features:

  • 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

Usage:

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();
})

Lambda Handler:

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 Routers 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 version
  • ApiGatewayLambdaRouter - API Gateway V1 REST APIs
  • ApiGatewayV2LambdaRouter - 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);
}

Custom Integration:

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

Installation

# 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

Building and Testing

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

About

A general purpose middleware, route modeling, and route matching framework. Includes Lambda router implementation

Resources

Stars

Watchers

Forks

Packages

No packages published