Ben Kehoe recently wrote a post about AWS API Gateway to Lambda integration: How you should — and should not — use API Gateway proxy integration with Lambda. In his post, Ben gave a few reasons why he believes using API Gateway Proxy Integration is an anti-pattern.
Ben does a great job summarizing how the integration works. He writes:
The pattern that I am recommending against is the “API Gateway proxy integration” as shown in the API Gateway documentation here. It involves three separate features:
• Greedy path variables: route multiple paths to a single integration
• The ANY method: route any HTTP method to the integration
• The Lambda proxy integration itself: instead of defining a transform from the web request to the Lambda input, and from the Lambda output to the web response, use a standard way of passing the request and response to and from the Lambda.
When these three features are put together, it can turn API Gateway into a passthrough, letting you use your favorite web framework in a single Lambda to do all your routing and processing.
Ben is a leader among serverless users and has great insights on the best approaches to building serverless apps. But I’m not sure I agree with the notion that API Gateway Lambda Proxy Integration is an anti-pattern. First, let’s look at his concerns about using the integration, then we’ll look at its benefits.
What’s “Wrong” With API Gateway Lambda Proxy Integration?
Ben lays out the following issues with API Gateway Proxy Integration:
- It bloats your Lambda with web logic
- It vastly increases your attack surface
- Your API is less self-documenting
Let’s look at each of these concerns.
It Bloats Your Lambda With Web Logic
Your Lambda gets bloated with all the code for multiple logical paths.
You’re paying for your Lambda to run logic that API Gateway will do for free.
Both are true, but only to a point.
Putting many code paths into one Lambda function may seem like an anti-pattern. It’s not common in serverless examples. But it’s not necessarily “bad”. Ben’s point here is that separating code paths into different Lambda functions can provide for separation of concerns. Separation of concerns stresses modularization to achieve units of code that are easily understood as independent pieces of functionality. But modern programming languages have advanced facilities for providing separation of concerns. For example, Node.js has its module system, and Java and C# have packages. It is easy to architect, develop, and test individual code paths, whether or not they share the same physical compute unit (i.e. Lambda Function).
As for paying for the run time for logical code paths, there is some amount of efficiency to be gained by offloading the logic to API Gateway. However, the cost of an HTTP framework layer can be fairly minimal, especially at high loads where Lambda Function runtimes are cached. When using JITted languages (e.g. Node.js, Java, or C#), the cost can be broken down into cold start and general execution costs. Execution costs are likely to be on the order of single digit milliseconds for a typical routing layer. Cold start costs are higher because you have to compile code once before execution the first time. But there are ways to mitigate the overhead of cold starts, and as we’ve seen with Auth0 Webtasks, cold starts can be eliminated in theory and likely will become less of an issue over time as AWS and other providers improve their FaaS offerings. In addition, one of the core benefits of serverless is it allows more focus on business logic instead of figuring out how to build a scalable system. It’s reasonable for serverless engineers to accept some drawbacks (e.g. cold starts and slightly higher average latency) in order to achieve greater development velocity and faster time to market.
It Vastly Increases Your Attack Surface
You’ve increased your attack surface by allowing more requests through API Gateway into your own code and by relying on additional 3rd party libraries — whose security you are responsible for.
There are two issues Ben is pointing out. The first is that API Gateway can block improper requests without invoking the backing Lambda functions. API Gateway can save Lambda invocation costs this way and can also offload request validation from your Lambda function. However, cost savings are dampened by the fact that improper requests are most often due to a bug in the requesting client, which can usually be fixed quickly either in the client or in the API. And while offloading request validation to API Gateway is nice, you have to do it in an API Gateway-specific fashion. Most HTTP frameworks have validation mechanisms built-in, many of which have more flexibility than API Gateway.
The second issue Ben points out is the security of third-party libraries. This is a serious issue. You are only as strong as your weakest dependency.
However, your weakest dependency is usually the code that is least reviewed and least used across projects. Usually this means your own code, then uncommon modules your code uses. In contrast, common HTTP frameworks are some of the most highly consumed and analyzed pieces of code. As an example, the Express framework is the 5th-most depended-upon Node.js package. While Amazon employs super smart security engineers to analyze their code, I’ll wager that virtually no one outside of Amazon has analyzed their API Gateway code. I don’t mean to suggest that Express is more or less secure than API Gateway, rather that they are both fairly secure solutions via different code analysis methods.
To Ben’s point, if you are deciding whether to use API Gateway’s routing mechanism or rolling your own HTTP routing layer, you are probably better off using API Gateway.
Your API Is Less Self-Documenting
You’re missing out on the API documentation features that API Gateway provides [if you use Lambda Proxy Integration].
API Gateway does have a commendable amount of functionality for API documentation. They make it possible to import and export Swagger API definitions. But they’re not the only game in town. For example, if you choose to write your Lambda integration using one of the many common Node.js HTTP frameworks, then you can also use the swagger module to do the same thing.
Benefits Of API Gateway Lambda Proxy Integration
There are many benefits of consolidating code into fewer functions. I’ve written about these benefits before in my Serverless Function Architecture Principles post. At a high level, there are performance benefits of consolidating functions (fewer cold starts), and there are development benefits when it comes to integration testing and managing systemic complexity. Using one function with an HTTP framework facilitates the goal of functional consolidation.
But let’s focus on the key power of serverless technologies, as noted above: they enable engineers to focus on the business logic instead of scalability logic. Most web app engineers are familiar with HTTP frameworks they have already used in the past. In this sense, API Gateway is another framework that can be learned and used effectively. But is it objectively better than all the pre-existing frameworks? Aside from splitting hairs on single-digit milliseconds of latency difference, there are many reasons why one framework may be better than another for a given project. This is the base reason why we have so many different frameworks already!
In short summary, use a common HTTP framework that best fits your project needs, which may or may not be API Gateway’s built-in routing mechanism.