Writing portable serverless applications
A question I sometimes get is how to build serverless applications that can easily be used in other environments, for example having the same business logic running on AWS Lambda and on Docker containers.
It is actually simple to write code that can be reused in multiple ways. The most important takeaway is to separate the Lambda handler from your core logic. The Lambda handler is the function, within your code, that AWS Lambda is calling when the Lambda function is invoked.
Use the Lambda handler as a wrapper for your business logic, managing the input syntax for the event, and eventually the output back to the AWS service in case of synchronous invocations, such as for the Amazon API Gateway.
For the sake of simplicity, I am going to use Node.js here for the practical examples, but something similar can, of course, be done with other Lambda runtimes, such as Java, Go or Python. This is also giving me a good excuse to play with the new Node.js 8.10 runtime now available in AWS Lambda.
For example, this is a simple “Hello World” Lambda function separating the business logic from the adapter interface:
The core logic is in the
greetingsFor function, that expects in input a
name and returns some
greetings. For example, it can return “Hello” and your name, or “Hello World” if no name is provided.
The adapter, in this case, is an event wrapper for AWS Lambda and is in the
handler function, where a
name is taken from the event and passed to the
greetingsFor function. The return of the function is then passed back to the Lambda platform.
What happens if I want to use a different event syntax? For example, let’s transform the above function into a web API that can be accessed via the Amazon API Gateway, or run anywhere using the Node.js Express framework:
The business logic function
greetingsFor is exactly the same as before, but now we have two different adapters:
handlerfunction is looking for the input
namein the query parameters of the event received from the Amazon API Gateway, using the default Lambda Proxy integration.
- If the code is not running on AWS Lambda, the Node.js Express framework is used to transform the Lambda function into a web application. Adding a
Dockerfileto containerize the web application is trivial.
In this way, the same code can be used for a Lambda function and for a traditional Web application. To check if the code is running on AWS Lambda, you can, for example, look for a couple of default environment variables available in the Lambda Execution Environment.
To run the previous function locally, just
npm install express before starting the application. Note that I am not importing the Node.js Express framework when the code is executed on AWS Lambda. This is a best practice to keep both your function code and memory footprint smaller.
If your application is using automatic tests, you can have them running on both AWS Lambda (in the cloud or using SAM Local) and a Docker container to continuously check that you are not creating, over time, dependencies on one of the different environments.
An even better approach is to split business logic and adapters (such as event wrappers and web interfaces) into different files. In the previous example you would have:
greetings.jsfile, with your business logic only, exporting the
event_APIGateway.jsfile, that contains the
handlerfunction for AWS Lambda (following the API Gateway event syntax) and require
greetings.jsas a Node.js module
app.jsfile, that provides a web interface using the Node.js Express framework, again importing
greetings.jsas a Node.js module
If you need more ways to use your business logic, you can add more adapters. For example, to use the same business logic triggered by other AWS services you can add
event_SNS.js, and so on.
In the case of a Kinesis Data Stream, data is processed in micro-batches and a single event usually includes more than one record to process. The
event_Kinesis.js adapter would apply your business logic to each of the records in input.
The next time you are going to create a new application, think of the different adapters you could need, and start by splitting the business logic from its interfaces. And don’t forget to share what you are going to build with me!