Product
Introducing License Enforcement in Socket
Ensure open-source compliance with Socket’s License Enforcement Beta. Set up your License Policy and secure your software!
simple-lambda-router
Advanced tools
Small router utility for lambda functions handling HTTP events from multiple resources and methods
When building an serverless application on AWS Lambda driven by events from API Gateway, there are several popular patterns:
GET
/POST
/PUT
/DELETE
requests related to a users
entity and another to handle requests for a todos
entity.For case 2 and 3 above, your lambda function will need to route the incoming request to the appropriate code to respond to it. This is what I built this little library for.
In addition to routing requests to the right handler, this package adds a few useful things:
Promise
when called.RouteError
class that is useful to reject requests with a specific status code.Content-Type
header indicating the body is JSON data. It will be parsed into a JS object before it is passed to you handler code.The functionality remains pretty much the same but the package has been refactored to work with the ES module syntax rather than CommonJS modules. If you upgrade, make sure you use import
rather than require
to get elements from this package and your handler files should also be ES modules.
The package exports a Route
function. It takes routing configuration and its return value is the handler of your lambda function. For example, if you have a Lambda function defined in your SAM template whose index property is set to index.handler
as exported by code in file index.mjs
, this is what that index.mjs
file would look like:
request.resource
:import { Route } from 'simple-lambda-router'
export const handler = Route(
{
resources: {
'<HTTP_METHOD>:<API_RESOURCE>': <FILE_HANDLING_THAT_REQUEST>
},
headers: {
'<SOME_HTTP_RESPONSE_HEADER>': '<HEADER_VALUE>'
}
},
<OTHER_ARGUMENTS_TO_BE_PASSED_TO_YOUR_REQUEST_HANDLERS>
)
request.path
:import { Route } from 'simple-lambda-router'
export const handler = Route(
{
paths: {
'<HTTP_METHOD>:<API_PATH>': <FILE_HANDLING_THAT_REQUEST>
},
headers: {
'<SOME_HTTP_RESPONSE_HEADER>': '<HEADER_VALUE>'
}
},
<OTHER_ARGUMENTS_TO_BE_PASSED_TO_YOUR_REQUEST_HANDLERS>
)
Files defined as handlers as defined in arguments to Route()
above must export a function called handler
and may also export a validate
object as follows:
import joi from 'joi'
export function handler(request, context, <OTHER_ARGUMENTS_FROM_CALL_TO_ROUTER.ROUTE>) {
return new Promise((resolve, reject) => {
resolve({
statusCode: 200,
body: {
someProp: 'the value'
},
headers: {
'<SOME_HTTP_RESPONSE_HEADER>': '<HEADER_VALUE>'
}
})
})
}
export const validate = {
queryStringParameters: <QUERYSTRING_VALIDATION_RULES>,
body: <BODY_VALIDATION_RULES>,
}
handler
The handler
function gets the request
and context
arguments as if they were handling the lambda function directly. It also gets all other arguments passed after the config in Route()
(see first snippet above).
Your handler function must return a Promise. It resolves to an object that must contains a statusCode
and body
property, it may also contain a headers
property:
body
is an object, it will go through JSON.stringify()
before being sent back as the response body.headers
property are included in the response in addition to those included in the initial Route()
call. If the same HTTP header is defined in both places, the value in the promise resolution of the handler has precedence.If your Promise is rejected or has uncaught exceptions, the router will send a 500 Internal Server Error
response back to lambda. You can reject with a specific HTTP error as follows:
import { RouteError } from 'simple-lambda-router'
export function handler(request, context) {
return new Promise((resolve, reject) => {
reject(new RouteError({
statusCode: 403,
message: 'Check with your admin to get access to this.'
}))
})
}
validate
The validate object lets you define validation rules for the request body and/or query parameters. Validation is super easy to use with the Joi Object schema validation library but you can use anything as long as you wrap it in a validate
method compatible with the Joi validate method.
import joi from 'joi'
export const handler = (request, context) => {
...
}
export const validate = {
// Validating using Joi library
queryStringParameters: joi.object({
status: joi.string().valid('opened','completed').required(),
}),
// Custom body validation/augment code
body: {
validate: (requestBody) => {
let error = null
if (!('requiredProp' in requestBody)) {
error = {
details: [
{ message: 'Request body must contain a "requiredProp" property.' }
]
}
}
let value = Object.assign({}, { propWithDefaultVal: 'default val' }, requestBody)
return { error, value }
}
}
}
request.path
If your API gateway has a proxy resource with a greedy path variable {proxy+}
, the request.resource
value will be that greedy path and the request.path
will be the actual path with no extracted parameters showing in request.pathParameters
.
Example:
Your API declares a
/{proxy+}
resource and receives aGET /items/34
. Upon receiving that request, API Gateway sends an object to your Lambda function with aresource
property set to/{proxy+}
andpath
property set to/items/34
.
This package allows you to define path-based routing. In other words, the routing logic looks at /items/34
from the example above, not /{proxy+}
as it would with resource-based routing. Note that your path route can contain named parameter the same way they are defined in an API Gateway resource path. Those parameters will be extracted and added in the pathParameters
property of the request object sent to your handler.
Example [ctnd]:
A path key of
GET:/items/{id}
will match the request from the example and your handler will haverequest.pathParameters.id
set to34
It is possible to handle requests to a path or resource through a chain of handlers instead of a single handler file. This is useful if many endpoints have common validations (e.g. handlers for /items/4
, /items/4/links
, and /items/4/foo
would all need to check if item 4 actually exist and return a 404 if it doesn't).
You chain handlers by setting the handlers as an array of files. Each step in the handler chain follows the same structure as a single-file handler (it returns an object with a handler
function returning a Promise and an optional validate
object). Validation rules are enforced for each step of the chain and each step gets the value the previous step resolves to as one if its arguments.
See examples under items-fn
or the test-07-chained-handlers.js
file for more details.
Example in the examples
directory are for the previous version of this package which followed CommonJS modules syntax. Looking at tests may be more useful.
FAQs
Small router utility for lambda functions handling HTTP events from multiple resources and methods
The npm package simple-lambda-router receives a total of 4 weekly downloads. As such, simple-lambda-router popularity was classified as not popular.
We found that simple-lambda-router demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Product
Ensure open-source compliance with Socket’s License Enforcement Beta. Set up your License Policy and secure your software!
Product
We're launching a new set of license analysis and compliance features for analyzing, managing, and complying with licenses across a range of supported languages and ecosystems.
Product
We're excited to introduce Socket Optimize, a powerful CLI command to secure open source dependencies with tested, optimized package overrides.