After building a couple of serverless online services such as SnapSecret, I've considered the use of Lambda Function URLs over API Gateways. After some experimentation and research, I've decided to share some of my findings in the form of a blog post. When building serverless APIs backed with Lambda Functions, there are currently 2 options AWS provides that can achieve this.
The Amazon API Gateway is a feature-rich service that allows you to create a RESTful API that can be invoked via HTTP, including features such as authentication, throttling, caching, logging, and monitoring. By making use of the configuration options provided, you reduce the amount of development effort required to build a robust and functional API.
In comparison, Lambda Function URLs allow you to set up a basic HTTP endpoint that can be used to invoke a single Lambda function. This is useful if you quickly need a way to invoke a Lambda function via HTTP, but don't need the full functionality of the API Gateway.
This table represents a comparison of the features natively (no additional logic needs to be implemented within your Lambda code) provided by these services
|Feature||API Gateway||Lambda Function URL|
|Private VPC Endpoints||✅||❌|
|Path based Routing||✅||❌|
* When configuring Authentication for Lambda Function URLs, you can only configure IAM users and roles authentication.
** Throttling can be performed by using the AWS Lambda provisioned concurrency feature, but you are not able to throttle per IP address or API key.
As you can see, the feature set provided by the API Gateway is much more extensive than the Lambda Function URL. Due to this, the use cases for both services are different and will vary based on your requirements. If you need support for any of the features outlined above, it would be better to rely on the API Gateway service rather than baking your own as the development cost will significantly outweigh the cost of using the API Gateway.
For the cost comparison, I will assume the following details in the ap-southeast-2 region:
Due to the lifetime free tier available for Lambda functions, executing the lambda function will not cost anything to operate in this case. Even if we exclude or go over the AWS Free Tier usage, this will cost a total of USD 1.45 per month:
1,000,000 requests x 150 ms x 0.001 ms to sec conversion factor = 150,000.00 total compute (seconds) 0.50 GB x 150,000.00 seconds = 75,000.00 total compute (GB-s) Tiered price for: 75000.00 GB-s 75000 GB-s x 0.0000166667 USD = 1.25 USD Total tier cost = 1.2500 USD (monthly compute charges) 1,000,000 requests x 0.0000002 USD = 0.20 USD (monthly request charges) 0.50 GB - 0.5 GB (no additional charge) = 0.00 GB billable ephemeral storage per function 1.25 USD + 0.20 USD = 1.45 USD
API Gateways come in 2 variations, the HTTP API and the REST API. A comparison of the two can be found in the AWS Documentation for Choosing between REST APIs and HTTP APIs. An excerpt directly from the documentation reads as follows:
REST APIs and HTTP APIs are both RESTful API products. REST APIs support more features than HTTP APIs, while HTTP APIs are designed with minimal features so that they can be offered at a lower price. Choose REST APIs if you need features such as API keys, per-client throttling, request validation, AWS WAF integration, or private API endpoints. Choose HTTP APIs if you don't need the features included with REST APIs.
I will provide the pricing for both HTTP and REST APIs, but will only be comparing the REST API as it is the more feature-rich option.
# HTTP API 1 KB per request / 512 KB request increment = 0.001953125 request(s) RoundUp (0.001953125) = 1 billable request(s) 1 requests per month x 1,000,000 unit multiplier x 1 billable request(s) = 1,000,000 total billable request(s) Tiered price for: 1000000 requests 1000000 requests x 0.0000012900 USD = 1.29 USD Total tier cost = 1.2900 USD (HTTP API requests) HTTP API request cost (monthly): 1.29 USD # REST API 1 requests x 1,000,000 unit multiplier = 1,000,000 total REST API requests Tiered price for: 1000000 requests 1000000 requests x 0.0000035000 USD = 3.50 USD Total tier cost = 3.5000 USD (REST API requests) Tiered price total for REST API requests: 3.50 USD 0 USD per hour x 730 hours in a month = 0.00 USD for cache memory Dedicated cache memory total price: 0.00 USD REST API cost (monthly): 3.50 USD
The cost difference for these services is not significant, but it is still worth noting that the Lambda Function URL is cheaper purely on a cost-per-request basis.
$1.45 USD for Lambda Function + $3.50 USD for REST API = $4.95 USD per month
Lambda Function URL
$1.45 USD for Lambda Function = $1.45 USD per month
API Gateways should be used when you need to build a robust and feature-rich API that can be invoked via HTTP. This is a good option if you need to support any of the features outlined above, support multiple Lambda functions or even proxying requests to other services. Enhancing the authorisation and authentication capabilities of your API Gateway can even be done via Lambda Authorisors to support custom authentication methods such as JWTs.
API Gateways are purpose-built with building APIs in mind, so anything more than a single purpose, simple API should be built using the API Gateway service.
Lambda Function URLs should be used when you need to quickly set up an HTTP endpoint to invoke a single Lambda function. This is a good option if you need to quickly set up an HTTP endpoint to invoke a Lambda function, but don't need the full functionality of the API Gateway. This can range from anything like a webhook or a single lambda function that requires HTTP invocation capabilities. Anything beyond a simple HTTP -> lambda invocation should be built using the API Gateway service, as the moment you require any of the features outlined in the features comparison section, you will need to build your logic to support them. This adds complexity and development costs that will outweigh the cost savings you will realise when using the Lambda Function URL.
To do a performance comparison, I have opted to use a tool called apache benchmark to send traffic at both endpoints and measure the response times. In the test, I used a concurrency of 10 and a total of 2500 requests from a single machine hosted outside of AWS. This low concurrency value is to avoid any HTTP rate-limiting that may occur when sending a large amount of traffic from the single machine as well as reducing concurrent lambda executions resulting in lambda throttling.
For the Lambda Function URLs, we were able to send 2000 requests in about 4 seconds with an average response time of 2.024ms/request.
sktan@testvm:~$ ab -c 10 -n 2000 https://basdf1234.lambda-url.ap-southeast-2.on.aws/ This is ApacheBench, Version 2.3 <$Revision: 1807734 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking basdf1234.lambda-url.ap-southeast-2.on.aws (be patient) Completed 200 requests Completed 400 requests Completed 600 requests Completed 800 requests Completed 1000 requests Completed 1200 requests Completed 1400 requests Completed 1600 requests Completed 1800 requests Completed 2000 requests Finished 2000 requests Server Software: Server Hostname: basdf1234.lambda-url.ap-southeast-2.on.aws Server Port: 443 SSL/TLS Protocol: TLSv1.2,ECDHE-RSA-AES128-GCM-SHA256,2048,128 TLS Server Name: basdf1234.lambda-url.ap-southeast-2.on.aws Document Path: / Document Length: 20 bytes Concurrency Level: 10 Time taken for tests: 4.048 seconds Complete requests: 2000 Failed requests: 13 (Connect: 0, Receive: 0, Length: 13, Exceptions: 0) Non-2xx responses: 13 Total transferred: 544767 bytes HTML transferred: 40897 bytes Requests per second: 494.06 [#/sec] (mean) Time per request: 20.240 [ms] (mean) Time per request: 2.024 [ms] (mean, across all concurrent requests) Transfer rate: 131.42 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 6 8 1.2 8 14 Processing: 5 12 3.9 11 54 Waiting: 5 12 3.8 11 54 Total: 12 20 4.0 19 64 Percentage of the requests served within a certain time (ms) 50% 19 66% 20 75% 21 80% 22 90% 25 95% 28 98% 31 99% 34 100% 64 (longest request)
For the API Gateway, we had very similar results with 2000 requests taking about 4.6 seconds with an average response time of 2.324ms/request.
sktan@testvm:~$ ab -c 10 -n 2000 https://basdf1234.execute-api.ap-southeast-2.amazonaws.com/testlambda-apigw This is ApacheBench, Version 2.3 <$Revision: 1807734 $> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Software Foundation, http://www.apache.org/ Benchmarking basdf1234.execute-api.ap-southeast-2.amazonaws.com (be patient) Completed 200 requests Completed 400 requests Completed 600 requests Completed 800 requests Completed 1000 requests Completed 1200 requests Completed 1400 requests Completed 1600 requests Completed 1800 requests Completed 2000 requests Finished 2000 requests Server Software: Server Hostname: basdf1234.execute-api.ap-southeast-2.amazonaws.com Server Port: 443 SSL/TLS Protocol: TLSv1.2,ECDHE-RSA-AES128-GCM-SHA256,2048,128 TLS Server Name: basdf1234.execute-api.ap-southeast-2.amazonaws.com Document Path: /testlambda-apigw Document Length: 20 bytes Concurrency Level: 10 Time taken for tests: 4.648 seconds Complete requests: 2000 Failed requests: 0 Total transferred: 382000 bytes HTML transferred: 40000 bytes Requests per second: 430.29 [#/sec] (mean) Time per request: 23.240 [ms] (mean) Time per request: 2.324 [ms] (mean, across all concurrent requests) Transfer rate: 80.26 [Kbytes/sec] received Connection Times (ms) min mean[+/-sd] median max Connect: 6 7 0.8 7 12 Processing: 9 16 4.1 15 65 Waiting: 9 16 4.1 15 65 Total: 16 23 4.2 22 72 Percentage of the requests served within a certain time (ms) 50% 22 66% 23 75% 24 80% 25 90% 29 95% 31 98% 34 99% 36 100% 72 (longest request)
The results are very similar, with the API Gateway being slightly slower than the Lambda Function URL though I assume this is likely to do with the additional processing that the API Gateway does when handling the request. If we have a look at the P99 values, we can see that there was only a 2ms difference for 10 concurrent requests. This tiny difference is not enough to sway your decision into choosing a Lambda Function URL over running an API Gateway to increase API performance and you would likely be able to find better results by optimising your code or running on a more recent Lambda Runtime version.
After the analysis, what I've found is that unless you don't need any of the features provided by the API Gateway both in the short and long term, you should be using the Amazon API Gateway service. The biggest benefit of using a Lambda Function URL is if you need to quickly set up an HTTP endpoint to invoke a Lambda function but without any of the additional capabilities offered by the API Gateway. An example of when this might be the case is if you were to build a simple standalone webhook that processes some information and returns some data to the caller. This could be a webhook that is used to process a payment or used to process a form submission (e.g a contact form). In this case, you would not need any of the additional features offered by the API Gateway, so it would be more cost-effective to use the Lambda Function URL.