-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add So long API Gateway, and thanks for all the routes draft
- Loading branch information
1 parent
cedd6b9
commit 8fc8dee
Showing
7 changed files
with
122 additions
and
0 deletions.
There are no files selected for viewing
Binary file added
BIN
+232 KB
...ng-api-gateway-and-thanks-for-all-the-routes/apigw-lfurl-clfurl-performance.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+224 KB
...sets/images/blog/so-long-api-gateway-and-thanks-for-all-the-routes/clefairy.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+737 KB
...o-long-api-gateway-and-thanks-for-all-the-routes/code-genie-max-description.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+238 KB
...blog/so-long-api-gateway-and-thanks-for-all-the-routes/cognito-jwt-verifier.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+192 KB
...og/so-long-api-gateway-and-thanks-for-all-the-routes/jwt-express-middleware.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added
BIN
+572 KB
...g/so-long-api-gateway-and-thanks-for-all-the-routes/lfurl-workaround-tweets.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
122 changes: 122 additions & 0 deletions
122
src/content/docs/blog/so-long-api-gateway-and-thanks-for-all-the-routes.mdx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
--- | ||
title: 'So long API Gateway, and thanks for all the routes' | ||
|
||
date: 2024-09-20 | ||
excerpt: | | ||
For nearly a decade I've been building APIs with API Gateway and Lambda. However, most of the time I just need a way to invoke a Lambda Function over HTTP with a custom domain name, and this is exactly what CloudFront + Lambda Function URL (CLFURL) enables. | ||
authors: | ||
- brett | ||
tags: | ||
- api-gateway | ||
- lambda | ||
- aws | ||
- cloudfront | ||
- serverless | ||
draft: true | ||
--- | ||
import { Image } from 'astro:assets'; | ||
import clefairy from '../../../assets/images/blog/so-long-api-gateway-and-thanks-for-all-the-routes/clefairy.gif' | ||
import lfurlWorkaroundTweets from '../../../assets/images/blog/so-long-api-gateway-and-thanks-for-all-the-routes/lfurl-workaround-tweets.png' | ||
import cognitoJwtVerifier from '../../../assets/images/blog/so-long-api-gateway-and-thanks-for-all-the-routes/cognito-jwt-verifier.png' | ||
import jwtExpressMiddleware from '../../../assets/images/blog/so-long-api-gateway-and-thanks-for-all-the-routes/jwt-express-middleware.png' | ||
import apigwLfurlClfurlPerformance from '../../../assets/images/blog/so-long-api-gateway-and-thanks-for-all-the-routes/apigw-lfurl-clfurl-performance.png' | ||
import codeGenieMaxDescription from '../../../assets/images/blog/so-long-api-gateway-and-thanks-for-all-the-routes/code-genie-max-description.png' | ||
|
||
For nearly a decade I've been building APIs with API Gateway and Lambda. I was even fortunate enough to be part of the team that launched API Gateway back in 2015! It's served me well and I'm sure I'll use it again on future projects that require some of its more advanced features. | ||
|
||
However, most of the time I just need a way to invoke a Lambda Function over HTTP with a custom domain name, and this is exactly what CloudFront + Lambda Function URL (CLFURL) enables. | ||
|
||
<figure> | ||
<img src={clefairy.src} alt="Clefairy" loading="eager" style={{margin: 'auto'}} /> | ||
<figcaption style={{ margin: 0, color: '#666', fontSize: '0.8rem', textAlign: 'center' }}> | ||
Clefairy: The unofficial mascot of CLFURL | ||
</figcaption> | ||
</figure> | ||
|
||
## Why change? | ||
|
||
### Cost | ||
Since launch, one of the biggest criticisms of API Gateway has been its pricing. One would expect that the cost of Lambda (the thing that's doing the heavy lifting) would make up the larger portion of the cost pie compared to the thing that's simply connecting it to a client. In reality, API Gateway costs often exceed Lambda by more than 2x. Under similar conditions, CloudFront costs ~10% of Lambda costs. | ||
|
||
:::caution[WARNING: Gross oversimplification to prove a point detected] | ||
Of course saying API Gateway is simply a service that connects Lambda over HTTP is unfair. <a href="https://x.com/theburningmonk" target="_blank" rel="noopener">Yan Cui</a> has an excellent comparison of the two approaches in his blog post <a href="https://theburningmonk.com/2024/03/when-to-use-api-gateway-vs-lambda-function-urls/" target="_blank" rel="noopener">When to use API Gateway vs. Lambda Function URLs | ||
</a>. He covers many of the advanced features of API Gateway and concludes with a preference for API Gateway in most scenarios | ||
::: | ||
|
||
The one area where API Gateway has a cost advantage is on unauthorised requests. When using the JWT Authorizer, API Gateway eats those costs for you. With CLFURL you would pay both the CloudFront and Lambda costs for unauthorized requests. This is really only a concern when it comes to DDOS (or Denial of Wallet) attacks. Fortunately, <a href="https://docs.aws.amazon.com/waf/latest/developerguide/ddos-event-mitigation-logic-continuous-inspection.html" target="_blank" rel="noopener">AWS Shield Standard</a>] (a free DDOS protection service included with CloudFront) *should* mitigate those attacks. | ||
|
||
|
||
### Max timeout | ||
|
||
API Gateway has a max timeout of 29 seconds, which is more than enough for most REST API needs. However, with AI becoming a core feature of many products, those extra 31 seconds afforded by CloudFront (a total of 1 minute) can be crucial. | ||
|
||
In fact, this is the main reason I started investigating this option in the first place! <a href="https://codegenie.codes" target="_blank" rel="noopener">Code Genie</a> uses AI to generate data models based on the description of your app. There is currently a 500 character limit on this description to mitigate the chance of it taking longer than 29 seconds. By moving to CLFURL, Code Genie will be able to handle significantly more complex requests, and enable other AI features. | ||
|
||
<figure> | ||
<Image src={codeGenieMaxDescription} alt="500 characters is not enough to describe complex applications" style={{margin: 'auto'}} /> | ||
<figcaption style={{ margin: 0, color: '#666', fontSize: '0.8rem', textAlign: 'center' }}> | ||
500 characters is not enough to describe complex applications 😞 | ||
</figcaption> | ||
</figure> | ||
|
||
:::note[API Gateway max timeout can be increased on request] | ||
API Gateway <a href="https://aws.amazon.com/about-aws/whats-new/2024/06/amazon-api-gateway-integration-timeout-limit-29-seconds/" target="_blank" rel="noopener">recently announced</a> it can increase the max timeout via support request. It's unknown if there are additional costs associated with this. | ||
::: | ||
|
||
### Performance? | ||
|
||
In my tests comparing the performance of API Gateway to CloudFront, I found both approaches yield similar results. Here are the highlights: | ||
|
||
1. CloudFront adds a shocking +300ms latency for cross-region (other-side-of-the-world) requests when not under load. These findings are why I didn't switch to CLFURL earlier this year. I recently decided to test the performance again, hoping that AWS had resolved the issue. They hadn't. However, after diving deeper I discovered that this latency is akin to a cold start. When under load, this extra latency is a rare occurrence. | ||
2. Under load, CloudFront offers marginally better performance than API Gateway. ~4% faster on average. | ||
|
||
<figure> | ||
<Image src={apigwLfurlClfurlPerformance} alt="API Gateway vs Lambda Function URL vs CloudFront + LFURL" style={{margin: 'auto'}} /> | ||
<figcaption style={{ margin: 0, color: '#666', fontSize: '0.8rem', textAlign: 'center' }}> | ||
API Gateway vs Lambda Function URL vs CloudFront + LFURL | ||
</figcaption> | ||
</figure> | ||
|
||
## Locking down the Lambda FURL | ||
|
||
If you want to enforce CloudFront security features such as Shield and WAF, you need to ensure attackers can't bypass CloudFront by calling your Lambda Function URL directly. To accomplish this, <a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-restricting-access-to-lambda.html" target="_blank" rel="noopener">AWS recommends</a> setting the LFURL's authorization to AWS_IAM and using OAC to grant access for the distribution. If only it were that simple. Unfortunately, there are two big limitations: | ||
|
||
1. CloudFront overrides the Authorization header when invoking your Lambda Function, so you need to use a different, non-standard header to include your JWT. | ||
2. The client needs to sign the payload in PUT/POST methods. See this post by <a href="https://speedrun.nobackspacecrew.com/blog/2024/05/22/using-cloudfront-as-a-lightweight-proxy.html#lambda-oac" target="_blank" rel="noopener">David Behroozi</a> for more details. | ||
|
||
A simpler solution proposed by <a href="https://x.com/ryan_sb" target="_blank" rel="noopener">Ryan Scott Brown</a> is to have CloudFront add a custom origin header with a secret: `'X-CloudFront-Secret': 'NoBadGuysAllowed'`. Your Lambda Function can check the secret in the header, and return an error code if it doesn't match. Another solution proposed by <a href="https://x.com/dreamorosi" target="_blank" rel="noopener">Andrea Amorosi</a> uses Lambda@Edge. | ||
|
||
<a href="https://x.com/ryan_sb/status/1835363459979960624" target="_blank" rel="noopener"> | ||
<figure> | ||
<Image src={lfurlWorkaroundTweets} alt="Tweets with alternative solutions for locking down Lambda Function URLs" style={{margin: 'auto'}} /> | ||
<figcaption style={{ margin: 0, color: '#666', fontSize: '0.8rem', textAlign: 'center' }}> | ||
Simpler solutions to locking down your Lambda FURL | ||
</figcaption> | ||
</figure> | ||
</a> | ||
|
||
## Replacing API Gateway Authorizer with in-Lambda JWT validation | ||
|
||
The only "advanced" API Gateway feature I use on all of my APIs is the Cognito/JWT Authorizer. Since CloudFront doesn't have a similar native feature, we need to perform the JWT validation inside the Lambda Function. This has the added bonus of making our app more portable and easier to run locally, while also granting us more flexibility. | ||
|
||
Performing JWT validation yourself is also incredibly fast (~4ms cold; ~0.3ms warm in Node.js) thanks to <a href="https://x.com/AWSbrett/status/1779422735539847454" target="_blank" rel="noopener">this tip by David Behroozi</a>. | ||
|
||
<figure> | ||
<Image src={jwtExpressMiddleware} alt="Express Middleware for JWT validation" style={{margin: 'auto'}} /> | ||
<figcaption style={{ margin: 0, color: '#666', fontSize: '0.8rem', textAlign: 'center' }}> | ||
Express Middleware for JWT validation | ||
</figcaption> | ||
</figure> | ||
|
||
<figure> | ||
<Image src={cognitoJwtVerifier} alt="Cognito JWT Validation via aws-jwt-verify" style={{margin: 'auto'}} /> | ||
<figcaption style={{ margin: 0, color: '#666', fontSize: '0.8rem', textAlign: 'center' }}> | ||
Cognito JWT Validation via aws-jwt-verify | ||
</figcaption> | ||
</figure> | ||
|
||
|
||
## Conclusion | ||
|
||
CloudFront + Lambda Function URL (CLFURL) is an excellent combination for Serverless APIs when you don't need any of the advanced features offered by API Gateway. With CloudFront being an order of magnitude cheaper than API Gateway, it puts it more in line with what'd you'd expect when compared to the cost of other components of your Serverless architecture (and without sacrificing performance). | ||
|