AWS Bites Podcast

139. Building Great APIs with Powertools

In this episode, we discuss using AWS Lambda Powertools for Python to build serverless REST APIs with AWS Lambda. We cover the benefits of using Powertools for routing, validation, OpenAPI support, and more. Powertools provides an excellent framework for building APIs while maintaining Lambda best practices.

AWS Bites is proudly brought to you by fourTheorem, an AWS Consulting Partner dedicated to helping you and your team deliver successful projects on AWS. With a sharp focus on performance and cost optimization, we’re here to ensure your cloud journey is smooth and efficient. If you’re searching for a trusted partner to bring your AWS projects to life, visit us at fourtheorem.com. and let’s work together to make your vision a reality!

In this episode, we mentioned the following resources:

  1. AWS Bites 41. How can Middy make writing Lambda functions easier?
  2. AWS Bites 120. Lambda Best Practices
  3. REST API - Powertools for AWS Lambda (Python)
  4. Hono
  5. Fastify
  6. Axum
  7. FastAPI

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:

Eoin
00:03:58

I haven't got to try Hono yet, but it seems really, really good. But I do like, you know, something that it depends on what kind of project you're building, but something that gets you going really quickly. And, you know, Python is generally a fast language to develop with, very productive. Everybody can get involved. And I really like the FastAPI approach as well. And the approach we're talking about today with Lambda is actually modeled on FastAPI. So it's very similar. And this is all about PowerTools. And we did mention PowerTools in the past, AWS Lambda PowerTools, mostly in the context of metrics and logging and tracing, because those were the three pillars that kicked off the whole PowerTools adventure. I think the first language was supported by, the first language supported by PowerTools was Python. Back when ATR-LESA kicked off the project, I know there's a whole team behind it now. And the amount of development that has been done on it for an AWS open source project is pretty astounding. And the quality and level of documentation is one of the best I've seen for any open source project. The Python one is by far the most fully featured version of PowerTools, because it was the first. In addition to the metrics, logging and tracing, it supports middleware, a bit like MIDI does with Node.js functions. And then you've got all sorts of other features, like types for Lambda events, parameter retrieval, feature flags, streaming responses, item potency support, and then you have validation and parsing. And there's a whole web framework essentially built into PowerTools for building REST APIs. So validation, parsing, REST APIs support kind of work together to give you this really nice API framework with a good developer experience. Should we talk actually, what does an API framework need to have? What would you like to see from it?

00:06:19

Other concerns could be serialization and diserialization. You will have requests coming in. You'll need to be able to process these requests. So most of the time it's probably going to be JSON, but JSON is not the only format. You might have like a form that is being submitted and you want to handle it as part of your API. You might have uploads of files. So there can be different kinds of encodings effectively that your API needs to support. A good framework should be able to give you the tools to process all the different kinds of data and turn them into something that you can actually programmatically use. And similar with responses, you might need to serialize an object into a specific response, serializing probably JSON again, but that's not necessarily the only option you might want to support. So again, the framework should give you all the tools for serialization and diserialization. Validation is a very related topic because when you are accepting data, it's always a good practice to do validation. So hopefully that needs to be part of the tools that the framework give you. And one thing that I really like a lot to see in frameworks, and this is actually something that is not always present. Generally, you need to rely on some kind of third-party plugin. That's the OpenAPI specification. So I think I like when a framework allows you to use types and strongly typed code in general in your code, and then it's able to build an OpenAPI specification, starting from your routing definition and from the types that you used in your endpoints. And I guess if you put all of that together, you can get to a point where you have nice types, autocompletion and type safety for all the requests and responses. I think that will be kind of the golden standard for me when a framework gives you all of that. I think you end up with a very nice developer experience and you can effectively develop your API with the confidence that you are managing the data properly. You're not going to have surprises where maybe you're trying to access a field that doesn't exist, or maybe you're going to return a response that doesn't necessarily match what you promised the user you would return. So I think that's kind of my ideal framework. How does PowerTool help in this sense? Does it match this definition or is it verified? Yeah, I think it hits everything you've mentioned.

00:10:56

And maybe, I don't know, storing it in a database record or something, which will eventually lead to subtle bugs here and there, because you have all these different objects stored with slightly different, I don't know, shape. So that's definitely one of the benefits of validation. Then the other thing is that if you have a very strict definition of what your input should look like, then you could also create strongly typed interfaces to represent that input, which in the language of choice, let's say Python in this case, that will give you nice auto-completion and type checking. So once you are at that point in your code when you are using a specific type and you have validated that the input matches the type, then everything else should get so much nicer because you can easily see all the fields available, autocomplete all the types and so on. So when you combine the two, effectively that reduces the risk of bugs because you're checking that the data makes sense. And then at that point, you have a strongly typed interface. And that's generally a problem that exists with languages that are dynamic like Python and JavaScript, where if you don't have the diligence of doing all this, defining the interfaces and doing a proper validation, that's generally a very common source of bugs. So that's why I think I wanted to stress a little bit more how important is this point. And then when it comes to Powertools, they put a lot of effort into trying to give you good tools to do all of these things in a nice way and without having to write a lot of code yourself. And in Powertools, they specifically leverage a library called Padantic.

00:18:26

So you can basically set up your API resolver with Powertools, and then you just decide which functions to attach it to. So the way we do it is we have basically a separate module outside of all of your Lambda handlers where your resolver is created. And then you can just import that into the Lambda handlers that you want to be part of that resolver and share the same routing mechanism. And then when it comes to generating the OpenAPI spec, for example, so that one of the issues is that if you've got single purpose functions, each function had its own resolver. So they didn't, they weren't aware of the whole API, so they couldn't generate a full spec. But if you have a common resolver declared and you just import that in each event handler, you can just have a script locally that will basically load all of your handlers. They'll all share the same resolver at just a local time or build time. Then you generate the OpenAPI spec in a script. And then when you deploy it, you can decide whether to package each handler into separate functions or all into one function. And then, so we've done multiple projects where we do this and you basically, you get, we've done it where you do the Lambda-lit approach, which actually can work very well at the start of our project if you want to iterate really quickly, because you only had one function to worry about. But we've also done it with single purpose functions and you just have this shared resolver and then you have multiple deployments. And then you have the whole build pipeline, which is generating the OpenAPI documentation and the client bindings. And it works with both approaches. So it doesn't have to be a Lambda-lit.

Eoin
00:20:32

There's nothing specifically in Powertools that gives you a local development experience out of the box, but it actually becomes really easy once you have this API resolver setup. It's just really well architected. And it becomes a very thin layer on top of your business logic, the API layer. It becomes really concise and very portable. So the way we would just do it normally is you have your handlers and these routes, and then all of your logic, like your services and your repository patterns and everything sit separately. You could actually easily migrate your whole API to a completely different framework if you wanted to. But when it comes to local development, obviously if it's fast API, you can just run it locally. You can do something very similar locally with Powertools. And when we are doing this approach, we generally just set up a local server script, which is like a simple Python Flask server, which has one root in it and matches every request, just as a very simple translation of the Flask request into a Lambda event, HTTP API or ALB event, and then invokes the Lambda handler's code directly. And then the Powertools API resolver will take the request from there, just as it does in a real Lambda environment. And it's just generally a few lines of code to do that. And then you've got a local simulation server that behaves like API Gateway and Lambda. And it can really speed up development because you don't have to deploy every time. I know even that some people are using Lambda Powertools in containers, like with Fargate. Because it has so many nice tools there, you can actually take a similar approach and you can just run a server like that, very lightweight proxy and forward onto AWS Lambda for Powertools, make use of the resolver. And then you get all the other nice features like tracing, metrics, logging, item potency, whatever. So it doesn't have to be with Lambda to get benefit for all this great work. Yeah, that's pretty cool.

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