Amazon API Gateway is a great service for creating REST APIs and, as a managed service, there’s no infrastructure to provision, manage, or patch. While it can integrate with several backend types, it’s frequently used to build serverless APIs that rely on Lambda for compute.
While Lambda concepts are relatively simple, API Gateway is a whole other matter. Given that it has been built to allow all sorts of possibilities, it’s a much more complex and sometimes confusing service to navigate. If all you want to do is expose Lambda code via HTTP, you’ll only actually need a small subset of its features. Additionally, you’ll find yourself frequently evaluating various architectural tradeoffs along the way that impact your entire approach to things.
For example, you might find yourself asking the following:
- How many Lambda functions should I use for my API?
- How many APIs should I create? What’s a Lambda “proxy” integration?
- How do I handle CORS?
- Should I define each path/resource in API Gateway or leave it to my code to route things where they need to go?
In this post, I’d like to speak to some of these questions and the tradeoffs behind them. While both serverless concepts and API Gateway can be applied to a wide range of applications, I’ll simply be speaking to the creation of serverless microservices/APIs on AWS that use Lambda and API Gateway.
A Brief Review of API Gateway
API Gateway lets you create a number of different APIs that consist of resources (or paths) and methods for those resources (GET, POST, PUT, DELETE, etc.). Each method has an integration, which associates it with something, such as a Lambda function. When a request is made to the API, the resource and method are inspected to determine which Lambda function is invoked and the output is returned to the client. Let’s dig further into these constructs and discuss some things you’ll want to be mindful of.
APIs
How many APIs should you create? It depends.
If you are re-architecting a SaaS application, for example, and breaking the app out into several microservices, it might make sense to create one API per microservice. Each API has its own endpoint and can be individually consumed. With multiple APIs, each one typically doesn’t have a whole lot of resources under it and it’s used for a specific purpose, such as interacting with order information.
Conversely, if you’re creating a more generic API that partners can use to fetch information from your systems, a single API may be sufficient and it could have several different resources and methods for the various pieces of data being made available.
When creating an API, also keep in mind that you’ll need to choose its endpoint type, which can be “Regional” or “Edge-optimized.” The regional configuration is a relatively new option and eliminates the built-in CloudFront distribution (CDN) that has traditionally been tied to an API. It’s intended for use cases where the consumers of the API are primarily in the same AWS region. This would include EC2 instances, containers, or Lambda functions relying on the API. Latency is reduced because requests bypass CloudFront entirely. It can also be a good fit for cases where you’d like to use a different CDN provider or have more control over the DNS for multi-region purposes. The Edge-optimized option, on the other hand, improves connection time for APIs that are consumed from geographically diverse locations.
Another API consideration is authorizers, which are specific to each API. They are a mechanism for authorizing requests to your APIs. You can’t share them across APIs, but you can use the same underlying Lambda functions if necessary and create identical authorizer configurations for each API.
Resources
You can define multiple resources (or paths) under an API and tie them each to individual Lambda functions or you can create one “proxy resource” (typically under the “root” resource of the API) that passes all requests under its path to the same Lambda function. This really comes down to whether you want to manage things at the API Gateway level or in your Lambda code.
By defining more in API Gateway, you may be able to take advantage of Swagger definitions and SDK generation.
If you handle the routing in your Lambda code, you benefit from not having to change things in two different places every time an adjustment needs to be made (in your Lambda code and API Gateway). It is simpler and often more natural for a developer to focus on the code they’re already working with from day to day rather than switching gears and updating API Gateway through CloudFormation, the console, or wherever else it’s being managed.
When creating a proxy resource, you can optionally leave preflight CORS handling to API Gateway. This assists with browser requests associated with Ajax calls. Doing so increases the performance of these requests and it’s slightly cheaper as Lambda doesn’t have to be invoked every time. Keep in mind that CORS headers don’t support multiple domains and API Gateway uses a wildcard by default; so if you’d like to lock things down to multiple specific domain names, you may want to have Lambda handle CORS requests and respond appropriately to the host which is being requested by the client.
Proxy resources are frequently used together with a few other API Gateway features: greedy path variables, ANY methods, and the Lambda proxy integration type. These are discussed below.
Methods
For each resource, you can define the HTTP methods that you’d like to receive requests for. An “ANY” method is also available in cases where you’d like to direct all methods to the same place. Again, it’s mostly a tradeoff of using API Gateway vs. Lambda code to route things properly.
Each method has an integration. Integrations with Lambda exist in two forms: proxy and custom. Note that this is something entirely different from the proxy resource mentioned earlier. Amazon’s documentation says:
“To build an API with Lambda integrations, you can use either the Lambda proxy integration or the Lambda custom integration. In general, you should use the Lambda proxy integration for a nimble and streamlined API set up while providing versatile and powerful features. The custom integration may be a better value proposition if it is necessary for API Gateway to pre-process incoming request data before it reaches to the backend Lambda function. However, it is a legacy technology. Setting up a Lambda custom integration is more involved than setting up the Lambda proxy integration and the existing setup is likely to be inoperable when the backend Lambda function requires changes in its input or output…
“For the Lambda proxy integration, API Gateway sends the entire request as an input to a backend Lambda function. API Gateway then transforms the Lambda function output to a frontend HTTP response. The proxy integration is most commonly used with a proxy resource, which is represented by a greedy path variable (e.g., {proxy+}) combined with a catch-all ANY method.”
Unless you have a good reason to do otherwise, use the Lambda proxy integration.
Lambda
As far as how you structure your Lambda packages, there are a few things to decide.
For each method you create in API Gateway (whether it’s under a proxy resource or not), you’ll need to choose a Lambda function. The same function can be chosen for each resource/method as long as it properly responds to each request. This works for functions that have a single entry point and dispatch things as they come in. That kind of routing may require some extra coding, but it would simplify things on the API Gateway side.
Alternatively, separate Lambda functions can be associated with each resource/method. These functions can either contain the exact code they need or they can share the same underlying code package and merely have a different “handler” as part of their configuration that specifies which function/method within the package to invoke.
Things to keep in mind when making these decisions: Lambda packages are limited to 50 MB (compressed .zip/.jar file), so break your package up into multiple Lambda functions if you’re approaching this limit.
Also, a lot of it is a tradeoff between ease of use and extensibility. For example, you could keep things simple and create one .zip file that contains 5 Node.js files, one for each resource (customer.js, order.js, product.js, etc.). Within each file, you could have a Javascript function or handler for each HTTP method. Multiple Lambda functions could then be created with this same .zip file as long as the handler for each one points to the part of the archive that is needed. This would keep things simple and it might be easier to make changes in one place to all aspects of the API, but it also means each function has code that is never actually used and the package is larger than necessary. You have to decide what’s important to you.
Security
If your APIs aren’t public, you’ll need to tackle authentication/authorization. By default, API methods are publicly available. You can authorize requests using a Lambda authorizer, IAM, or a Cognito user pool. Lambda authorizers give you a chance to write custom authentication code and make use of JWT, OAuth, or any other security mechanism you want to use. One thing to know is that you can only choose one authorization approach per HTTP method. So if you’d like to allow multiple, you’ll need to use a Lambda authorizer and write code that takes that into account.
Limits
You’ll want to take a look at the soft and hard limits of the service and make sure they will work for your use case. For example, although Lambda has a max execution time of 300 seconds, API Gateway limits you to 29 seconds and this cannot be increased. Also, API Gateway does not support wild-card subdomains and not every aspect of the Swagger specification is supported.
Conclusion
API Gateway has a robust feature set and works great for serverless microservices. Keep these things in mind as you implement it. Again, most of it comes down to how simple you’d like to make things vs. how full-featured your requirements are. By making some decisions and standardizing on them, you’ll be able to rapidly roll out and iterate on your APIs.
If you have questions or need help with API Gateway—or anything else AWS related—contact us at info@1Strategy.com.