AWS Bites Podcast

74. Function URLs vs API Gateway

Published 2023-03-31 - Listen on your favourite podcast player

How can you use a Lambda to respond to an HTTP request? There are more ways than ever to do it. We have API Gateway REST APIs, Lambda support for Application Load Balancer, and now Function URLs. But which one should you use, and when?

In this episode of AWS Bites podcast, we will give you a quick and simple guide to picking the best way to build APIs with Lambda. In this video, we're going to pitch Function URLs against API Gateway in a battle for the ages!

Function URLs offer a simple and quick way to get a public URL to invoke a Lambda function, with fewer configuration options and cheaper pricing. They are suitable for private webhooks, simple backend functions, and machine learning inference backend. However, they lack authorization and DDoS protection, making them unsuitable for like public webhooks. On the other hand, API Gateway offers more features and control, making it suitable for public APIs. API Gateway comes in two flavors: REST and HTTP with some subtle differences.

Finally, we will also cover Application Load balancer and explore when and why it can be a convenient alternative to both Function URLs and API Gateway.

AWS Bites is sponsored by fourTheorem, an AWS Consulting Partner offering training, cloud migration, and modern application architecture.

In this episode, we mentioned the following resources:

Let's talk!

Do you agree with our opinions? Do you have interesting AWS questions you'd like us to chat about? Leave a comment on YouTube or connect with us on Twitter: @eoins, @loige.

Help us to make this transcription better! If you find an error, please submit a PR with your corrections.

Luciano: People have been building APIs and web books on AWS Lambda for many years now. There used to be one way to do this with API Gateway REST APIs. Then came Lambda support for Application Load Balancer and then the new HTTP APIs in API Gateway. And the latest way to build simple APIs backed by Lambda is Function URLs. And you don't even need API Gateway or load balancer for that one. But should you use it? And if so, when?

Today we will give you a quick and simple guide picking the best way to build APIs with Lambda and pitch Function URLs in a battle against API Gateway. And also we're going to mention the pros and cons of load balancers in between. I am Luciano and I'm here with Eoin and this is AWS Bites podcast. AWS Bites is sponsored by fourTheorem. fourTheorem is an AWS partner for migration, architecture and training, including APIs for application and system integration. Find out more at fourTheorem.com. There is a link in the show notes. So since Function URLs is the new feature here, what is it and why it's something that we should be excited about?

Eoin: With Function URLs, the focus is on trying to make almost any configuration go away. So if you have a function built in Lambda, the goal here is to give you the quickest way of getting a public URL to invoke that function. All of the other methods you talked about, like API Gateway and load balancer, they require you to create and configure lots of new resources and configure them and understand how those configurations are syntaxed.

And you have to then integrate them with the Lambda service. With tools like SAM and the Serverless Framework, it makes that process easier, but there's still all those knobs under the hood and you kind of have to understand how they work. So Function URLs are a different approach. It's not even a simple separate service. It's just a feature of the Lambda service itself. So you only have to enable it with one property.

And once you enable it, you get a generated URL right away for the function that can be invoked publicly. So the format of that URL you get is like HTTPS, a generated ID, dot Lambda URL, dot whatever region, dot on dot AWS. So it's different to the ones you've seen in API Gateway. Now, I did say that the aim was for zero configuration. There are a few parameters you can add if you want. So one is you can enable course.

So if you've got a front end and you need to have course enabled for your API, you can do that. You can also enable IAM authorization. So while the URLs are public, you can restrict access to them by mandating IAM privileges. And that's the only supported authorization method for Function URLs. Another one you could do is you can actually map your Function URL to a specific Lambda function alias. So that means you have a label that points to a specific version of that Lambda function.

And if you have safe deployments where you're shifting traffic from one version to another, you can do that using aliases in a controlled way and then point your Function URL to a specific alias. So what this simple configuration means is that you don't get a lot of the features that are supported with API Gateway. We could probably just quickly list through the features that you're going to be missing out on that you get with API Gateway, but don't come with Function URLs.

So API keys are out. You can also don't have the option of using Cognito User Pool or Lambda Authorizers or JWT Authorizers. You have no support for custom domains, so you are restricted to using the generated names that you get. You don't get request or response validation. You don't get any caching. And if you want a web application firewall, there's no support with Function URLs directly. Now, you can use CloudFront in front of a Function URL if you want and get caching from CloudFront.

You could also use that for WAF as well. So that's another way of overcoming the caching and WAF limitations in Function URLs. You don't get HTTP access logs with Function URLs, which might be a bit of a deal breaker for some people. And you have no way of doing throttling or usage plans. And of course, there's no WebSockets support like there is with API Gateway. So those are all the things you're missing out on. So given that that's quite a long list, what do you think? Why would you use it? What are the actual advantages of Function URLs, Luciano? And what kind of use cases might people say, OK, they don't have all the features, but I could still use Function URLs here?

Luciano: Yeah, I think as you said, if you don't need most of these features, there are some trade-offs for which I think it might be beneficial to consider something like functional URLs. First of all, it is much simpler. So if you just need a quick way to trigger a Lambda based on a URL, that's probably the simplest way that there is right now. It is also cheaper. It is actually free compared to all the other options where you have an extra cost other than the cost of the Lambda invocation.

In the case of Function URLs, you don't have that extra cost. You just pay for the Lambda duration and the invocation. Also, there is an interesting article. We're going to put the link in the show notes that claims that this integration with function URL, it is the fastest one. And it claims something around 8.30 something milliseconds with a warm Lambda. That of additional overhead just in doing that HTTP request.

But we were not actually able to replicate that number. And I think the best that we got in our tests was 100 milliseconds. Maybe that really depends on the testing methodology. It depends on the region. There might be many factors to affect really that particular result. We'd be curious to know more. So if you have any more data that can prove or disprove this hypothesis, definitely let us know in the comments.

Another thing, and this is actually a really interesting one that may enable some interesting use cases, is that timeout is much longer than API Gateway. In fact, in API Gateway, you have 30 seconds, meaning that if your Lambda doesn't respond between 30 seconds, API Gateway will just stop the connection there and respond with an error. With Function URLs, instead, you can leverage the entire maximum direction of a Lambda.

Because if your Lambda is lasting the maximum amount of time, which is 15 minutes, that connection is still going to be open. So you could use this particular feature if you have APIs that, for whatever reason, need to take more than 30 seconds to create a response for the user. And actually, with that, let's move on to some use cases. So we can try to think a little bit more practically what we can use this feature for.

And the first use case, and probably the one that will come to mind first for most people, is Webhooks. Because Webhooks, traditionally, you just need a URL and that creates an integration point between different systems. And for instance, I do not. The common ones I've seen is CRM systems. They generally support Webhooks. So maybe you would want a way to know when a new deal is created in the system, because maybe you want to do some, I don't know, data enhancement.

Maybe you want to use another system or another API to try to enrich the data. You could create your own Lambda with a Function URL and then create the Webhook integration using that Function URL. And very similarly, I've seen newsletter systems doing the same thing. Basically, every time somebody signs up for your newsletter, you can have the option to specify a Webhook URL there, and then you can run your own backend logic to do something with it.

Maybe you can insert that email address into a specific database, maybe a marketing database or something like that, or maybe you can just send a custom newsletter to welcome the person into the system. Another one that I've seen is shipping providers. So if you are running an e-commerce that needs to send physical products, very, very often they will provide a Webhook-based interface to give you updates every time there is a status update on one of your shippings.

So you could create a Lambda with a Function URL, and then you can receive those kinds of events and use those events to update your own database and maybe send notifications to your customers to tell them what's the status of their shipping. And finally, another one that I've seen a lot for Webhooks is integration with CI systems or repositories, where they also can trigger Webhooks based on different events.

So if you want to do something custom, again, you can just build your own Lambda very quickly, Function URL and integrate it that way. Another use case could be outside Webhooks when you are building quick prototypes. And this can be also something that long term, maybe you want to do something like API Gateway, but you are just sketching out a Lambda just to see if the idea works. And maybe you are even using the web console.

It could be very beneficial if you already have your cURL request ready or maybe post map request ready there to just quickly enable the Function URL just to get a URL that can invoke your Lambda, do the request, test that everything is actually working as you expect, and then you can switch it off and maybe before going to production, actually migrate to API Gateway to leverage all the other features.

Another use case, and this is something that actually we found in the blog post announcement for Function URLs, is what they call single function microservices, which actually in the blog post didn't really explain what they meant with that example. But later on in the same blog post, they mentioned the idea that you could create a payment endpoint for a mobile application. So maybe that's what they were referring to as a single function microservice.

And I think that makes a lot of sense if you also consider that you can use IAM authentication there, so you could create a very small service. It's just an endpoint. You don't need to create an API Gateway dedicated to that because you can secure it with IAM and then you can call it from other services. And then I was trying to think for other possible use cases and I was thinking the different features that you have with Lambda Function URLs.

And since you can enable CORS, I think that enables a bunch of use cases where you have very simple front ends, maybe static websites, and you want to create some server-side functionality. Again, that could be a contact form or maybe a form to sign up for a newsletter and you can build all the server-side logic using a Lambda with a Function URL. And finally, again, thinking about that 15 minutes time, extra time that you can use compared to API Gateway, I was thinking that that could be a good use case if you have an API that runs some machine learning inference algorithm that maybe can take longer than 30 seconds, maybe can take a few minutes, and you still want to use that HTTP protocol there.

If you use Function URLs, then you can still use a Lambda to do all this work behind the scenes. So, of course, these are just some examples. If you have other ideas, we'd be curious to hear what you're thinking about in the comments, so definitely let us know. But it's always worth keeping in mind what are the limitations there, because in general, you can build all these different use cases, but you need to think that you have no authorizations, at least outside if you use IAM, but most importantly, you don't have DDoS protection.

So that basically means that if somebody manages to figure out what's your Function URL, and if you're using that, for instance, BI in the frontend application, it will be there in the HTML, so it's not something that you can easily keep secret. So what they could do is they can start to generate requests against that particular URL and basically do a denial of wallet attack to you, because you might end up paying for all of those invocations.

Now, we were trying to think, okay, are there strategies there that you can put in places to protect yourself against that? And maybe to some degree, what you could do, you could create an alarm to detect excessive invocations and then basically respond to that alarm by editing the Lambda function and setting the throttling to basically forbid additional invocations of that Lambda. And that could work, but you have to keep in mind that under the alarm, you have to and that could work, but you have to keep in mind that at that point you are stopping that Lambda entirely, even for legitimate invocations.

So you are literally deciding, let's sacrifice this feature for now in exchange for not getting the denial of wallet attack. So again, if you are really worried about DDoS, probably Function URLs are not the best option there. The final point that I have is in terms of drawbacks of Function URLs is that there is no routes or HTTP methods. Again, it's just a URL. You get a custom domain and every request there will invoke the Lambda.

So if you are expecting a post and somebody does a GET, you still get the Lambda invocation. Now, of course, you can do validation inside the Lambda function, but you have to remember that the Lambda gets invoked. So you're going to be paying for that, which is something that you can avoid instead with API Gateway because you can be very specific on which methods and paths will actually invoke the specific Lambda. And finally, the Function URL, since it's generated randomly, if for any reason you have to delete the stack, redeploy, so you're basically removing the Lambda and recreating it, you will end up with a new URL. So if you are assuming that that URL is going to be stable across the entire deletion of the stack and recreation, that's not going to be the case. So if you happen to do that, you will need to reconfigure whatever integration because you will have to provide a new Function URL. Yeah, that's a good point.

Eoin: And there's no real way around that. I think you can't use a CNAME because I presume the service uses the host header to do the routing back to the individual function. So given these drawbacks, let's kind of pitch Function URLs in a battle against the rest. We don't want to cover API Gateway in extensive detail, but we can remind ourselves of the options and the trade-offs. Now, API Gateway has two flavors.

We have the classic REST API mode and the newer HTTP API. HTTP API is about 30% of the price of REST API roughly, but there are some feature gaps. So it's a bit leaner. It doesn't have all the features of REST API. We should mention you can also do WebSockets with API Gateway, but we'll leave that out since that's not really part of our use cases here. And we'll assume that you're dealing with GETs, POSTs, PUTs, patches, and that sort of typical stuff.

So HTTP API, I would say, is the nearest competitor to Function URLs because it's a bit simpler and the price and performance are better than REST API. In terms of authorization, we mentioned that Function URLs supports IAM. HTTP API will support IAM Lambda authorizers as well and JWT authorizers. And those JWT authorizers can also be used with Cognito User Pools, whereas the REST API has a dedicated authorizer type just for Cognito User Pools.

The older REST API also has a private mode, so you can use it for internal APIs. But that's not always plain sailing. If you want to use custom domains with private internal APIs, that's not very trivial and it requires a workaround with a load balancer in front. Otherwise, if it's not a private API, HTTP APIs and REST APIs both support custom domains with your own domain names. So that handles the case you mentioned, Luciano, where you delete the function, redeploy it.

If you've got custom domains, then you can map that to the underlying API and you don't have to worry about regenerating URLs. But HTTP API and REST API both have throttling support. They both have the concept of stages, so you can have like a production stage and a development stage, QA stages, and they both have some level of open API or swagger support. Now, REST API does have a bunch of extra features that HTTP API does not have, like edge deployments.

request response validation with JSON schemas, and also request response transformation with velocity template language or VTL. This actually gives you the option of excluding Lambda altogether and having a functionless API. And you can proxy your API into a backend service like DynamoDB or Kinesis or some other underlying service. And that can be optimal in terms of cost and performance for some cases.

REST API also has caching and it has X-Ray tracing support. HTTP API doesn't have X-Ray support yet. So that's a pretty important one, I think, for a lot of people. I would say that the general guidance is use HTTP APIs if you don't need anything in that list, because REST API has a lot of those features, but they're not required in all cases. Edge deployments, not particularly. You can always use CloudFront as an option instead of edge deployments anyway. And request response validation and transformation. I don't see it used a lot in the wild, really. They're a little bit of niche in terms of their usage. So I would say also as a point of note on observability, both of these API gateway modes will give you logs, access logs, and you can configure that log format. It's worth mentioning that although you don't get any access logs with function, you're also you do get CloudWatch metrics. So you get the request count, request latency, and metrics for 400 hours and 500 hours as well.

Luciano: Yeah, so my understanding is that the general guidance seems to be that you should be using Function URLs if your use case is very, very simple and you don't really have a lot of patience for configuring a bunch of other resources just to route traffic to a Lambda. And that's probably a good use case if you are just learning AWS and these are your first experiments with Lambda and you just want something simple that just works.

You are not too concerned about extremely production ready deployments at that stage. If that's not your case, probably your second best choice is to use HTTP APIs. But of course, this is the case when you don't need caching, you don't need private endpoints, X-Ray validation, transformations, and all the other extra features that are not currently supported by HTTP APIs. Because if you need those, of course, you need to fall back to more traditional REST APIs on API Gateway.

But we still have left out another contender there, which is using an Application Load Balancer. So let's try to have a quick overview on why that might be an interesting option. And to be fair, in general, it's not too common to see load balancers being used with Lambda. They are more used for traditional EC2 deployments or Fargate deployments. But it is definitely possible to use load balancer to forward traffic to Lambda, basically to Invocate Lambdas with an HTTP event.

And there are some advantages actually in doing that. For instance, if you use API Gateway, something that I don't think is very commonly known, there is a limit of 10,000 requests per second. There is some burst capacity there, but there is a limit anyway. While if you use a load balancer, you can literally have millions of requests per second. Now, I don't know if that's really that useful in the context of a Lambda, because in the context of a Lambda, you always need to keep in mind that if you have many concurrent requests, that would probably trigger many concurrent invocations of different Lambda instances.

So basically, you might run out of concurrency in Lambda anyway at some point. So I don't think you can easily leverage millions of requests per second by just using Lambda behind the scene, unless you really had a lot of conversations with AWS and they managed to increase your quota significantly. But that probably only happens if you really have a very large business running lots of infrastructure in AWS.

Interesting enough, ALB and general use with EC2, as we said, so you have options to route traffic in many different ways. For instance, you can look at the hostname, you can look at headers, you can look at query strings. So in the case where you have complex microservices, where you have different routing logic, I think ALBs are the most flexible option there to decide where to send specific requests.

And we will talk more about an example that can be relevant there. And finally, another one is about pricing, because if you have very little traffic with load balancer, there is a specific pricing model where basically it might be a little bit more expensive to just have an ALB to route traffic if you don't really have a lot of requests. But when you start to have a significant amount of requests, I think that price is actually much more justified and overall you might end up with a better pricing compared, for instance, to just having API Gateway.

So keep in mind that also pricing might be interesting depending on what's your particular need there. Just to mention that particular example that I was saying before, one good use case is when you are trying to migrate a monolith application, maybe behind an Application Load Balancer, to something that looks like microservices and maybe you want to use Lambda as part of this new microservice architecture.

What you could do, you could still keep your own monolith and apply the strangler pattern to slowly migrate bits and pieces of that application to microservices. So basically what you could do as soon as you implement a new endpoint with a Lambda, you can just change the routing logic at the load balancer level and use the load balancer itself to map that particular endpoint to a Lambda invocation.

So that way you basically can do everything at the load balancer level, manage all that routing, keep using the monolith as long as we have features to migrate over to serverless functions and then at some point you will have only serverless functions in a microservices way. So just to summarize, we have four options. We have Function URLs, which are the simplest and the quickest. Very good for webhooks, very good for quick experimentation, very good if you are learning Lambda and you want a simple option just to trigger HTTP requests and invoke Lambdas.

Then we have HTTP APIs where you can leverage some of the more advanced features of API Gateway, but they are not as feature complete as REST API Gateways. So if you need all the additional features, of course, go to REST API Gateway. And finally, we have Application Load Balancer, which are generally very good either when you are migrating from monoliths or where you have a lot of traffic and therefore it might be more beneficial in terms of pricing and scalability to use an Application Load Balancer. We also have a repository where we put some of these notes and some of the evaluation tests that we made to prepare this episode, so we will have the link in the show notes. And this is everything we have for today. So really looking forward to your opinion, which HTTP integration method to use. And if you have any other suggestions on the we missed some other reason to use one approach rather than another, please let us know in the chat. Sorry, in the comments and we'll be back to you and trying to debate whether we like this opinion or not, or whether we are missing something there. Thank you very much and we'll see you in the next episode.