Lambda API: v0.10 Released

Lambda API v0.10 adds ALB support, new execution stacks for method-based middleware, full support for multi-value headers and query strings, and much more.

Posted in #lambda-api

Lambda API v0.10 adds the ability for you to seamlessly switch your Lambdas between API Gateway and Application Load Balancers. New execution stacks enables method-based middleware and more wildcard functionality. Plus full support for multi-value headers and multi-value query string parameters.

I'm really excited about this release and the possibilities it opens up for serverless developers. There are a few more features that are still on the roadmap, but I'm hoping to bump this up to v1.0 for the next release.  🙌

Thoughts, feedback, and contributions are always much appreciated!

NPM: https://www.npmjs.com/package/lambda-api

GitHub: https://github.com/jeremydaly/lambda-api

ALB Support

AWS recently added support for Lambda functions as targets for Application Load Balancers. While the events from ALBs are similar to API Gateway, there are subtle differences that require code changes based on your implementation. Lambda API now detects the event interface and automatically normalizes the REQUEST object. It also correctly formats the RESPONSE (supporting both multi-header and non-multi-header mode) for you. This allows you to call your Lambda function from API Gateway, ALB, or both, without requiring any code changes.

If are using (or intend to use) the Strangler Pattern to direct different parts of your application to Lambda, ALB is a much less disruptive (and cost effective) approach. You no longer need to use API Gateway to proxy all your traffic, which means lower latency and no routing changes to your existing application. ALB support in Lambda API makes it easy to build your applications using either API Gateway or ALB, and then run them both simultaneously without any additional code changes. The REQUEST object also added the interface property that your code can use to determine if the request came from api gateway or alb.

While similar, ALB events do not contain all of the same headers as API Gateway (such as clientType and clientCountry), but Lambda API provides defaults for seamless integration between the interfaces. ALB also automatically enables binary support, giving you the ability to serve images and other binary file types using Lambda API's built-in binary support features.

Lambda API reads the path parameter supplied by the ALB event and uses that to route your requests. If you specify a wildcard in your listener rule, then all matching paths will be forwarded to your Lambda function. Lambda API's routing system can be used to process these routes just like with API Gateway. This includes static paths, parameterized paths, wildcards, middleware, etc.

Multi-Value Header and Query String Parameter Support

AWS added support for Multi-Value Headers and Query String Parameters that allows you to parse multiple query string parameters and headers with the same name, plus the ability to send multiple headers with the same name. Similar functionality was also made available when using AWS Lambda with an Application Load Balancer. Lambda API now fully supports multi-values from both sources and gives you easy interfaces to manage them.

The REQUEST object now contains multiValueQuery and multiValueHeaders that always returns property values as arrays. Even if you are using ALB without multi-value support, these objects will still be correctly populated. The standard query and headers properties are still available and will continue to return strings. If multiple values are sent, headers will be merged per rfc2616 4.2. Message Headers, and query params will store the last value.

Sending multiple headers is just as easy. Lambda API v0.10 has updated header management methods that allow you control over exactly what gets sent to the client. You can still add and overwrite headers using the header() method, but you can also append values to existing headers by passing true as the third parameter. You can still retrieve a specific header with its string value using the getHeader(key) method. Or you can pass in true as a second parameter and retrieve the values as an array. You can also use the new getHeaders() method to retrieve all headers with their array values.

Do you want to send multiple cookies? Lambda API's cookie() method now appends by default, allowing you to set and send as many cookies as you'd like. And reading cookies continues to be a snap with the REQUEST.cookies property.

Method-based Middleware and Execution Stacks

Lambda API v0.10 introduces Execution Stacks as a way to more efficiently process middleware and route handlers. Execution stacks are automatically created for you when adding routes and middleware using the standard route convenience methods like get() and post(), as well as with METHOD() and use(). This is a technical implementation that has made method-based middleware and additional wildcard functionality possible.

Execution stacks are backwards compatible, so no code changes should be needed when upgrading from a lower version. The only caveat is with matching middleware to specific parameterized paths. Path-based middleware creates mount points that require a defined method to execute. This means that a /users/:userId middleware path would not execute if you also defined middleware with a /users/test path.

Execution stacks allow you to execute multiple middlewares based on a number of factors including path and method. For example, you can specify a global middleware to run on every /user/* route, with additional middleware running on just /user/settings/* routes, with more middleware running on just GET requests to /users/settings/name. Execution stacks inherit middleware from matching routes and methods higher up the stack, building a final stack that is unique to each route.

javascript
const middleware1 = (req,res,next) => { // middleware code } const middleware2 = (req,res,next) => { // middleware code } const globalMiddleware = (req,res,next) => { // execute on every request } // Execute middleware1 and middleware2 on /users route api.get('/users', middleware1, middleware2, (req,res) => { // handler function }) // Add global middleware api.use(globalMiddleware) // Execute middleware2 on /posts route api.get('/posts', middleware2) // Add middleware1 and handler api.get('/posts', middleware1, (req,res) => { // handler function })

Definition order also matters, meaning that routes defined before global middleware will not have it as part of its execution stack. The same is true of any wildcard-based route, giving you flexibility and control over when middleware is applied.

For debugging purposes, a new REQUEST property named stack has been added. If you name your middleware functions (either by assigning them to variables or using standard named functions), the stack property will return an array that lists the function names of the execution stack in processing order.

javascript
// From the examples above // GET /users stack: ['middleware1','middleware2'] // GET /posts stack: ['globalMiddleware','middleware2','middleware1']

Additional Updates

Lambda API v0.10 also added some additional enhancements:

Better wildcard routes for Single Purpose Functions

Building functions that respond to a single route is a common pattern for serverless applications. Lambda API now supports wildcard routes for ALL methods, which will respond to any request (regardless of the path). This allows you to use API Gateway or ALB to route the requests without needing to hard code paths in your Lambda functions.

Updated TypeScript definitions

Some additional TypeScript definitions were added to cover more parts of the application. There is still more work to be done, and the community is continuing to help and support these enhancements.

Added sourceIp fallback

In certain situations, including local test environments, the x-forwarded-for headers aren't always available. Lambda API now performs a fallback that will populate the REQUEST object's ip property with sourceIp if the x-forwarded-for headers are not provided.

Multi-value query string and interface detection support for logging

Default logging now contains passed query string params as arrays when multi-value support is detected. The event interface (api gateway or alb) is also recorded.

sendStatus method added

You can now use the sendStatus method to return an HTTP status code along with its standard status description. The status code map is based on the values provided by the IANA Status Code Registry. The functionality is also used to provide status descriptions for ALB interfaces.

Full Release Notes: https://github.com/jeremydaly/lambda-api/releases/tag/v0.10.0

NPM: https://www.npmjs.com/package/lambda-api

GitHub: https://github.com/jeremydaly/lambda-api