As Python 3 has eclipsed the old constraints of Python 2 and the web frameworks that adopted them,
we've seen a big jump in the new frameworks appearing on the scene,
taking full advantage of things like type hints, async it away, and more.
No framework has done this more successfully than FastAPI recently.
That's why we're here with the creator of FastAPI, Sebastian Ramirez,
to dive into this exciting new Python web framework.
This is Talk Python to Me, episode 284, recorded July 23rd, 2020.
Welcome to Talk Python to Me, a weekly podcast on Python,
the language, the libraries, the ecosystem, and the personalities.
This is your host, Michael Kennedy.
Follow me on Twitter, where I'm @mkennedy.
Keep up with the show and listen to past episodes at talkpython.fm,
and follow the show on Twitter via at Talk Python.
This episode is brought to you by Linode and us.
Python's async and parallel programming support is highly underrated.
Have you shied away from the amazing new async and await keywords
because you've heard it's way too complicated or that it's just not worth the effort?
With the right workloads, a hundred times speed up is totally possible with minor changes to your code.
But you do need to understand the internals.
And that's why our course, Async Techniques and Examples in Python,
show you how to write async code successfully as well as how it works.
Get started with async and await today with our course at talkpython.fm/async.
Sebastian, welcome to Talk Python to Me.
Thank you very much.
Thank you for the invitation.
It's a great pleasure to be here.
Well, thanks for building a cool web API.
You're welcome, I guess.
Yeah, it's going to be fun to talk about it, actually.
I think, you know, it's been really interesting.
There were a lot of web frameworks that came out in 2005, 2008 timeframe.
Then I feel like it kind of stabilized for a long time.
And then I'm not sure exactly what it was,
but I think that the async and await stuff coming along
sort of pushed the creation of a bunch of new frameworks.
And all of a sudden there was another framework every other week.
It was crazy.
And yours was kind of in that second wave or whatever wave that was.
And yeah, it looks like it's getting a lot of traction.
So that's awesome.
Yeah, thank you very much.
Yeah, I actually feel pretty much the same.
I feel like there were like a couple of waves of frameworks and then a second one.
And I was actually avoiding building a framework for a long time.
Yeah.
Yeah, I can imagine.
I definitely want to ask you about that.
But let's start with your story.
How'd you get into programming in Python?
So I'm not sure if I got to do some Python when I was little.
But when I really got into it was in, I think, when I was 20 something,
I was studying a bunch of courses online.
And one of them was, I was doing a bunch of courses,
actually doing everything in JavaScript on the browser,
because Node.js was just starting to be something.
And one of them required Python for doing artificial intelligence and stuff.
And I ended up like, yeah, let's just try.
Let's see if I can do it.
And I ended up just like learning the basics of Python for the course.
And it was super intense, but I ended up loving it.
And yeah, I just went on with Python for everything.
Oh, that's great.
You know, Python is one of those things that learning Python is really interesting,
because it's both super approachable, but kind of never ending.
Like, I think one of the guests I had on not too long ago,
I told a joke, talked about a t-shirt that was kind of a joke that said,
I learned Python.
It was a great afternoon or something like that, you know?
Yeah.
Which, in one hand, like, you can learn the syntax and you can make things happen
or follow along in a simple example.
But, you know, I'm still learning Python all the time, right?
Like, learning about your framework, I learned a bunch of stuff that I needed to figure out,
because it depends what you call Python.
Do you call Python, is that the language?
Or when you learn Python, are you also talking about learning the standard library?
Or if you really want to blow it up, then like the stuff on PyPI, right?
Like, you know, there's these different levels.
And you hear people say, oh, I learned Python really quick.
Or I've been learning Python for a long time and I'm still working on it.
Those people are probably not talking about the same thing, right?
Yeah, absolutely.
And then you get into numerical computation and processing stuff on GPUs.
And it's like a whole different story additional to everything else.
Yeah, absolutely.
Yeah.
Oh, for sure.
For sure.
Yeah.
And I only bring that up because I know there's a lot of people who are like
learning Python and getting started.
And they're probably a lot of times they're like,
I'm still working on it after a year.
You know what I mean?
And I just, it's really interesting that people say the same thing.
And there's like these layers that they're talking about.
Cool.
So you got into programming or Python dragged into the data science side of the world.
That's cool.
And what are you up to these days?
So right now I'm working for Explosion in Berlin, Germany.
Explosion are the creators of Spacey, the natural language processing package for doing things
like machine learning over text.
The Prodigy, the package for doing data annotation using active learning.
So to annotate machine learning.
So to annotate machine learning datasets.
And think the deep learning library for doing, well, deep learning using ideas from functional programming, type annotations, and all these things in a way that is compatible with other tools like PyTorch, TensorFlow, etc.
This is the company I'm working for.
I'm working on the team that is building Prodigy Teams.
So the version of Prodigy for Teams to have like multiple people in the same company interacting with each other, coordinated and doing all that is like the cloud version.
But the important thing is that it runs on the client's cloud, on the specific company's cloud.
So data is like stored and all the privacy stuff can be like safe and all the stuff.
Yeah.
So that's what I'm doing like mainly in Explosion from time to time.
I also help put some extra things from the other projects.
And as part like of, I have the privilege that part of my work is maintaining my open source tools and projects because we use them internally and because Explosion is very cool, I guess.
Yeah.
But yeah, like I get to spend like, I don't know, a fifth of all my working time just dedicated to this open source tool.
So yeah, that's mainly my day to day flow.
I know that sounds really fun.
And yeah, Explosion AI seems like a cool company.
You know, we just had Ines on Python Bytes on episode 191.
So a little while ago when this ships, but you know, in calendar time, not long ago.
Yeah.
I'm guessing that you are probably using a lot of working a lot on the API side of that product, right?
Exactly.
Yeah.
Mainly on that side.
Cool.
Well, speaking of APIs, let's start there.
Obviously, it's all about consuming APIs these days.
APIs are everywhere.
You go to Zapier and they're like, we integrate with, you know, however many thousand different
API endpoints that you might want to work with.
And it's, it's all pretty crazy.
But of course, creating APIs is super important.
So focusing on that side, how have you seen the evolution of API frameworks coming on?
Because the early days that I spoke of, it was like things were web frameworks.
And then if you wanted, you could somehow manage to put a web API in it.
Yeah, absolutely.
Exactly.
I think that's the key difference.
I think that's exactly the key difference.
Yeah.
Because.
Yeah.
So for example, this framework is basically, I don't know what you call it.
I would think it was something like API first.
I mean, obviously it's in the name, but there's a bunch of them like that these days that are
coming out where it's the building blocks talk in terms of APIs, not in terms of web templates
and whatnot.
Yeah, exactly.
So like, I guess for a very long time, the more established frameworks in Python specifically
have been Flask and Django.
And for APIs, it will be in Django, it will be Django REST framework.
In Flask, there are a bunch of plugins that can be combined together to make something that
works very well.
The same as with Django REST framework.
But the thing is that, as you were saying, these frameworks were made mainly to handle templates
in the backend.
So the extra plugins and the extra functionality was added on top around the ways that the framework
was able to do things.
So, and as you were saying, like there was this bunch of extra frameworks that came afterwards,
like several, even at the same time, sort of.
And then all the async wave came with a bunch of other frameworks.
Past API ended up later in that last wave, I guess.
But it was mainly from the learnings of all these previous frameworks.
I was using a bunch of those frameworks for a long time.
A bunch of plugins, a bunch of combinations, trying one thing, trying the other.
I had like something that was kind of stable, but it's still very difficult to maintain and
quite fragile.
Yeah, you said that you didn't really want to build FastAPI, but finally you decided,
all right, I need the thing I want to exist.
So here we go.
Yeah.
I felt like the meme of, hey, I built another JavaScript framework.
This happens like every week or so.
And I was trying really hard to avoid that.
And I was trying like, no, I just find the thing that I'm looking for and find the thing
that will do the functionality that I need.
Yeah.
And at some point when I was like, yeah, I'm not finding the right thing.
I found it.
And it was API Star was the name.
Right.
API Star was a framework built by Tom Christie, a great guy, the same creator of Django REST
framework.
Yeah, exactly.
A successor Django REST framework, but from scratch.
Exactly, exactly.
And that was API Star.
And API Star was trying to be compatible with Woosgi and Asgi.
So like the canonical standard specification or interface for web frameworks, which is what
Flask and Django are based on.
And at the same time with Asgi, which was the new standard that was also born at Django for
doing web sockets and all these asynchronous things.
Right.
It's probably maybe worth just pointing out to the people listening who are not deep in
the web hosting side, like Woosgi is this common API that all the different web frameworks,
Flask, Django, and so on, can talk to or implement.
And then all the web servers like MicroWisgi, G-Unicorn, and so on, know how to talk to anything
that does Woosgi.
And that's how you can run all the different frameworks on these various web servers.
But none of those were capable of supporting async programming, which is super important
for scalability on the server side, because just the way that thing was written is incompatible
with that.
And so there's another standard, I think, yeah, maybe Tom was even partially involved in like
defining the standard, I'm not sure, but called ASGI for async gateway interface.
And that's the ASGI that you're talking about, right?
So API Star was trying to do both of those things.
Yeah, exactly.
And complementing on what you were saying, like these specifications of how to interact
with the server and the framework are actually like quite simple.
It's mainly kind of one page.
And it just says like, there has to be a function that is going to be called with these parameters.
And it's basically that.
But then defining what is the shape of that function, what are the parameters that it's going
to receive, all that is like the main point of interaction between a server like Unicorn and
a framework like Flask.
So this new standard ASGI is the one that adds support for async and await and all these things.
And API Star was trying to have support for both things while being an API first framework
and having like a bunch of extra features that Tom Christie added.
And it was great.
I was just trying to add some authentication ideas to be able to integrate them with the
API first things.
And at that point, Tom Christie was also building Starlet, which is the micro framework slash toolkit
for building APIs in ASGI, in this new building APIs.
No, sorry.
For building like web applications or for doing web stuff using this new standard.
So it's like the bare bones thing is kind of in the middle of Flask and a lower level tool.
It will do like kind of the same things.
And because he was focusing on that, he had to deprecate most of the API Star components,
like the server components, and made it just like a system and a set of tools to validate
schemas for APIs.
Right.
And at that point, I just had found the perfect tool and then it had to be deprecated.
So I guess that was the cue.
That was the point where I said like, okay, let's try this.
And at the same, you know, that is a little bit unfortunate, but at the same time, you now
have Starlet.
So you don't have to write that layer.
You can work a little bit higher level.
So, well, let me just put my take on an API for building APIs, a meta API.
They're on top of Starlet, right?
Exactly.
Yeah.
So Starlette is like one of the fastest or I guess the fastest framework for Python currently,
like as measured by independent benchmarks.
Right.
Tech Empower is one of these people.
Yeah, exactly.
They measure not just, you're not saying that for Python frameworks, right?
That's like a general statement for many of the web.
They test all the different web frameworks, Python, Ruby, .NET, Java, and so on.
Yeah, exactly.
And a bunch of languages.
And with a bunch of combinations of databases and stuff.
So they have a great job.
And they do like testing rounds on physical servers.
So it's like a very, very robust and well-made general benchmark.
So it's quite nice because lets you like see more clearly what are the things and the advantages
and disadvantages.
Of course, benchmarks are always somehow biased in one way or another and difficult to handle
and to interpret.
At least like it's a very common ground, I think, these ones.
Right.
So then Starlette was this new tool and it was built by Tom Christie, the same creator
of Django REST framework, the same creator of the thing that I thought was the perfect
match that I was looking for.
So it was like a very, very solid option to build something on top.
Yeah.
I remember API Starlette being really nice.
I was sad to see that it got semi-deprecated as well.
Yeah.
I know.
But I mean, if we have FastAPI, because I think that FastAPI is actually better.
So it sounds to me like you're pretty happy with your job at Explosion AI, but you did have
this, you did run across this interesting job ad asking and you sent out a tweet and oh my
goodness, did that tweet take off.
So it said that it was looking for somebody to work in FastAPI and it suggested that they
have at least four years of experience with it.
Right.
Tell us about this.
Yeah, exactly.
It's fun because I thought like, huh, this is kind of a funny story.
And actually I have seen several job posts requiring similar things like for FastAPI specifically
requiring several years of FastAPI and FastAPI has one year and a half of existence.
And I saw the job post and I was like, it's kind of fun, but not fun enough.
But one day I was like, yeah, let's just tweet it.
It's not really that fun, but whatever.
So the tweet was, I saw a job post from four plus years of experience with FastAPI and
I couldn't apply because I only have 1.5 years since I created that thing.
I'm like, yeah, well, maybe it's time to reevaluate that years of experience is equal to the skill
level.
It was actually, I wrote it and I was like, it's not that fun.
And I asked my wife and she said like, yeah, well, whatever, just post it.
Yeah, exactly.
Someone might find it fun.
And then like two hours later, it was like 2000 likes or something like that.
And it started growing like crazy.
You probably almost had to like turn off notifications on your phone at that point.
If it's just ding, ding, ding, ding, retweet, retweet, like retweet.
Yeah, exactly.
Like I tried to open the Twitter app in my phone.
And for two days, it was just crashing immediately.
It wouldn't even open.
And I didn't even know, like, how is this thing?
This is so weird.
And if you see my Twitter account, you can see like the tweets before that or after that.
Right, right, right, right.
30 likes, 50 likes.
Yeah, you really struck a nerve.
Yeah.
You really found like this idea of like, oh yeah.
I guess so.
We all hate this idea and it's all the time, right?
Pretty funny.
Yeah.
This portion of Talk Python to Me is brought to you by Linode.
Whether you're working on a personal project or managing your enterprise's infrastructure,
Linode has the pricing, support, and scale that you need to take your project to the next level.
With 11 data centers worldwide, including their newest data center in Sydney, Australia,
enterprise-grade hardware, S3-compatible storage, and the next-generation network,
Linode delivers the performance that you expect at a price that you don't.
Get started on Linode today with a $20 credit and you get access to native SSD storage,
a 40 gigabit network, industry-leading processors, their revamped cloud manager at cloud.linode.com,
root access to your server, along with their newest API and a Python CLI.
Just visit talkpython.fm/Linode when creating a new Linode account and you'll automatically
get $20 credit for your next project.
Oh, and one last thing.
They're hiring.
Go to linode.com slash careers to find out more.
Let them know that we sent you.
Created about a year and a half ago.
Let's talk about some of the core features because I really, really like the way that you put this
together.
It brings together two or three of my really favorite things about some of the language stuff.
Type Annotations.
It makes those like first class citizens in a, the right way to put it, a non-artificial way.
Because I've seen people on other frameworks use Type Annotations, but the types are meant to talk to the
framework, but they're not actually the types you end up with.
You know, like think SQLAlchemy.
Like you would say that by default, the type is a column type.
But really what you mean is it's an integer.
So, I mean, that's not SQLAlchemy doesn't do that, but there's some web frameworks that kind of have
that similar take.
So Type Annotations, the async and await stuff on the server.
And then also this, this ability to have like a model that defines what you expect to be coming
in and the ability to not have to do that juggling yourself.
Yeah.
Exactly.
I think you, you already defined it better than me, but yeah, that was the idea.
I wanted like coming from what I had seen on API star and with the idea that you could maybe
add some extra information about the parameters that go into a function in the same declaration
of those parameters.
Then I wanted to extend that a bit.
And that's when I found Pydantic, which is a library for doing data manipulation, which is
awesome independently of if you use FastAPI or not.
It's great by itself.
And the way it works is that it's pretty much the same as with data classes, but it adds the
additional data validation and data serialization.
So if you type something that is declared with a type of an integer and you try to parse something
that is not an integer, it will try to transform that into an integer and try to serialize that
and convert that to the actual integer that you're declaring to receive.
So for example, if you say that parameter in a, in a URL should be an integer, let's say the user ID,
a Pydantic can take that and transform it into the, an actual integer so that the, the code internally
receives an actual integer.
Right.
So FastAPI uses all these ideas from Pydantic and the internal tools to integrate that with the framework itself based all on top of Starlet.
And as you say, I was trying to hit exactly that point of being able to use the type annotations in the standard way to use the standard type annotations.
For example, to use, if I wanted to have a string to declare it as a string, not having to import some specific strange class that was declared by FastAPI or something,
but just using the standard string as a type annotation and they get all the type support in the editor,
like auto completion and type checks and all these things, having them by default working as it will be with any code base that is type annotated.
So like all those features and on top of that, having the data validation,
so that data is valid at the point that your code starts to run.
You don't have to do all that validation by hand and data serialization.
So for example, like with Pydantic, you can create, say, a user class and say it has an ID colon int and a name,
which has a default value.
And then another thing like a date time, which has an optional of, say, date time and so on.
And if I'm reading it right, the ID that doesn't have any default value,
but is also not an optional ID is a required field.
Probably the name that has a default value.
Exactly.
Is optional and would take that default value.
And the optional one that's explicit also would have an optional and so on.
So like if there's a concrete type, it's both required and converted to that.
If there's a default value, it is that type, but it's also, it's not required and so on.
So there's a lot of nice nuance with almost nothing but like this field is this type and either is optional or not.
Right?
Yeah, exactly.
And the idea is to like use the same, the same way that standard Python works.
So like the same way that you declared that a function parameter is required, it should still be required, which is like by not declaring a default value, then it is required.
So it's interpreted that way.
So the idea is that it just like hooks the standard intuition and uses that to provide all the extra features.
So it should be just like very intuitive to work with and just work well by default.
That's kind of the...
It's beautiful.
And then also because of the way that it's put together, it leverages all the stuff that tells the editors what to do.
So if you get your value and you say, you know, dot, it knows what type that is and what to autocomplete right there.
Exactly.
And because you can also have like very deeply nested and complex data structures.
Like you can say that your API is going to receive a list of orders of food.
And each one of these food items is going to have a name, but can also have a list of ingredients, which each of them is going to be a string.
So it's like a very nested and complex to even to explain a complex data structure.
But then being able to have autocompletion, even for the deeply nested little bits inside of the editor and type checks and all that.
Like for me, that's like, wow, this is so cool.
It's so cool.
And like, it's the fidantic that I use.
Yeah.
You know why it's so cool is because that's the part of programming that just sucks.
Like you just, it's not fun to go, just go like, okay, does it have this?
Does it have this?
Is it this the type?
I tried to convert it.
No, it crashed.
Okay.
There's not much value in that, but you have to be very careful and specific.
And just, if that could just go away like this and something else handles it, it's beautiful.
Yeah.
And also I think it's like, at least in my experience, it has been one of those pieces of code and logic that is in many cases neglected, at least in the beginning.
Like when you say, okay, let's build an API.
Then you just add Flask and yeah, it works.
But then you're not taking into account all the corner cases and all the invalid data problems and all those things.
And it's very easy to miss that and to not realize that you have to validate all that stuff.
So having something that kind of enforces you to do it and does it by default, that's, I guess, like the main benefit.
Right.
And so when that fails validation, what happens?
Does the client get a 400 bad request with some kind of message about how it failed?
Yeah, exactly.
So when you declare, so let's say you create a class, a user class that inherits from this thing from Pydantic.
And you say like, I'm going to receive a food that has a name and has some ingredients.
And you declare the parameter inside of the function with using standard type annotations using the food colon.
This is of an instance of this class food.
So you have like standard Python type annotations and your code will be executed.
Like the code inside of the function that will handle that request is going to be executed only after the data is validated.
That's fantastic.
So if someone sends, yeah, I think that's pretty cool because it saves like, I don't know, 20, 50 lines of code inside of each one of those functions that is just done automatically.
So when data is invalid and it gets to the server, data is going to be validated and all the errors are going to be like checked and the client is going to receive.
It's a 422 error.
Yeah, it's a 400 range error.
422 is the unprocessable entity.
And it's going to tell the client exactly where is the error.
So it's not only saying there's an invalid data in your request.
It's saying exactly inside of this point deep in your deeply nested data structure, like one of those ingredients in the food items that you sent in your orders.
One of those ingredients is not really a string.
So exactly there is a problem.
Wow.
Yeah, that's awesome.
I love it.
And you said it, but I'm not sure if it totally sunk in.
All you have to do to make this happen is you create an API endpoint and then you say it takes a class, an instance of a class.
So like it takes a food colon food and that's it.
You don't have to like get from some dictionary and like copy it over or like FastAPI will look at that and say, oh, it's the thing that I have to supply this function or call it is this.
So I'm going to take the posted JSON data.
I'm going to run it through Pydantic, make sure everything works.
And in that case, I'm going to deliver to them to start off a fully validated one of these things, pre-populated strings converted to integers as they need to be and all that.
Right?
Yeah, exactly.
Exactly.
And if you're not going to do that, then you just won't receive it and the client will receive an error automatically.
So it's just works by default.
That's super cool.
I guess the other point around one of these core features here that we're talking about, this isn't the thing necessarily that like made me go, yes, this is awesome.
But it's nice is that it's API first.
So if I was in, say, Flask, I would say at app.route, give it the URL.
And then optionally, I could decide to filter down the HTTP verbs I could accept.
I could say, is it methods equals get comma head or something like that, right?
Those could be the things.
Yeah.
But with your API, it's app.get URL or app.post.
And so you speak in terms of, I want to handle this verb and this URL here, which is a very API way of thinking about the world.
Yeah, exactly.
And the idea with that was that in many cases, it's very easy to end up having a bunch of logic to handle get and post in the same function.
And then all that logic is actually like very cumbersome to untangle because it's handling different ideas and different cases.
It might share some code, but it's mostly different ideas in most of the cases.
Yeah, I've seen it too.
And it's such a bad idea.
It's like, why would you say if method equals get, here's part of my method, else if it's post, do this.
Just make that two methods and just set the method, the verb to be the two things.
In which case, like what you've got here, it just leads you more naturally down that path of this is the one that does the get.
This is the one that does the post.
And it kind of, there's this cool concept of designing APIs or ways of consuming stuff that I like,
like helping users fall into the pit of success.
Like if you don't know what you're doing, you kind of bumble your way around.
You're going to land into the place you should land.
You got to fight to get out of that place to like do it wrong.
You know what I mean?
I hope so.
I really hope so.
And this helps people fall into the pit of success.
And it's also that I waddled around into the neighboring places that didn't work very well.
Yeah.
And then like learning from these things, learning from all these best practices from different APIs, from different tools, from different plugins.
It's like all that learning is what I try to put together with FastAPI.
Yeah, well, well done, I think.
So because all these things we talked about are the stuff that you've put together, but because in terms of defining the API, but because it's based on top of Starlet, Starlette itself has a bunch of cool features as well, right?
Yeah, exactly.
Starlette is, well, to start, FastAPI is actually kind of a thin, sort of thin layer on top of Starlet, adding the pydantic parts and combining it all together based on the standards, based on the open API, JSON schema, or two, all these standards from the web that you just like get by default when using, when creating a FastAPI application.
But Starlette is the thing that is actually doing all the web handling.
It's the thing that it provides all the performance.
And Starlette has support for, so FastAPI also has support for web circuits.
There's easy GraphQL integration.
You can have templates the same as you would with Flask or with many other frameworks.
So you can actually build a web application based on templates in the like same way as you will do with all their established frameworks.
It's just that that's not where it shines.
But yeah, like you can have all these different things by default just because it's based on Starlet.
So if I wanted to rewrite my website, most of which is in Pyramid, some of which is in Flask, I could do it with FastAPI?
Yeah.
Actually, I imagine you will have like a bunch of Jinja 2 templates or something like that.
Then you can just preserve the same templates and you just reuse them and pass them to FastAPI.
It's just that there's not that huge benefit from that because like the logic will end up being very similar and FastAPI won't provide a lot on top of what other frameworks made with templates will do.
But yeah, you can actually do it fairly easily.
Yeah.
Okay.
Yeah.
I mean, we do have a bunch of APIs for like our mobile apps and stuff and it'd be nice to write those differently.
Yeah.
Yeah, absolutely.
Yeah.
Cool.
So there's a bunch of features you get from Starlette and because this is a layer on top of that, you basically get all the features of Starlette Plus.
We mentioned ASGI a couple of times.
Yeah.
And so I'm guessing I could do an async def view method or API endpoint.
Yeah.
So Starlette is based on this standard ASGI.
So you actually can get like all the things that are compatible with ASGI and there's a bunch of tools and additional like middleware and things that you can integrate directly because it's based on one of these common standards.
Like it's an open standard that everyone can implement.
So you can add, I don't know, Sentry, you can add the Data Dog, you can add Elasticsearch, like the logging functionality.
You can add like a bunch of things right there that are not necessarily even made for FastAPI or for Starlet, just because they adopt the same standard.
You can get like all that as well.
Okay.
Super cool.
And do you support async methods?
Oh yeah, of course.
So FastAPI is actually like mainly, well, and actually Starlette is mainly an async framework.
So you will use async def for your functions.
If you want to have like all the performance and you know how to use async and await.
Async and await are like provide a bunch of performance and performance in concurrent scenarios, a bunch of performance benefits.
But they have like their own ideas that you need to be aware of to be able to use them properly.
So you need to be able to use tools that are compatible with async and await.
So if you are using async and await, then you can take all the benefit from that.
But with the Starlette and FastAPI, you can also use standard function definitions.
And then it will do like the smart thing and will run those standard function definitions underneath in a thread pool.
So the technical details is that it won't block the event loop.
But the thing is that it won't degrade performance or it won't do something that actually ends up like hurting the whole application.
So if you are not sure or if you are doing, I don't know, machine learning stuff, then you can just use a standard function definition with just def whatever.
But when you want to or when you need to have some of these async ideas, for example, when you need to handle WebSockets, then you can use async def functions.
Oh, that's really nice.
So you don't have to adopt the async APIs in order to work with it, but you can if you want to.
Yeah, exactly.
And for example, you can if you have a bunch of SQLAlchemy models with a Flask application or something else, then you can just reuse exactly the same SQLAlchemy models and reuse even the same database.
So you can actually have both applications running at the same time and then just use a standard code as you would with normal def functions.
Very nice.
I think most people probably don't need that, right?
Like most of the time, APIs are fast enough and they're just fine and whatnot.
But when you do need them, it's really awesome that that capability is there to say the answer is not I have to go rewrite this in Node.js or in Go.
It is I just need to use a different thing, HTTP client that I'm consuming my API with like HTTPX or something that supports async.
Or I need to use a slightly different ORM library that supports async await, right?
Like a small adjustment rather than a throw it all away and start over.
Exactly, exactly.
And in fact, you can combine those in the same application.
So you can have like a bunch of standard function definitions for most of the application.
And then you need one that needs to have like all the maximum possible performance.
You can have like, I don't know, maybe literal SQL statements connected to an async postgres driver or something.
And then do that only for that specific endpoint that needs to have like all the maximum performance.
And then the rest, you can just do the standard code.
Right.
And in FastAPI, there's a dependency injection system that can be used to handle data positions and a bunch of things, but also to do things like authentication, authorization, getting, for example, getting the current user from a request, things like that.
And it's all gets integrated into the automatic user interface that is generated.
But the cool thing is that you can actually combine async and normal function definitions for these dependencies that can depend on one another.
So you can have different async and normal function definitions even for the same route that you're handling, even for the same endpoint.
Oh, wow.
Okay.
That's pretty awesome.
Talk Python to Me is partially supported by our training courses.
How does your team keep their Python skills sharp?
How do you make sure new hires get started fast and learn the Pythonic way?
If the answer is a series of boring videos that don't inspire or a subscription service you pay way too much for and use way too little, listen up.
At Talk Python Training, we have enterprise tiers for all of our courses.
Get just the one course you need for your team with full reporting and monitoring.
Or ditch that unused subscription for our course bundles, which include all the courses and you pay about the same price as a subscription once.
For details, visit training. talkpython.fm/business or just email sales at talkpython.fm.
One thing I think would be fun to do is maybe compare this to Flask at an API level.
And there's a cool article.
I don't know.
Have you seen it?
This article called FastAPI for Flask users?
Yeah, yeah, yeah.
It got a lot of attention.
And it was actually very on point with most of the things like how they are different.
Yeah.
And the advantages and stuff.
Yeah.
So this is an article by Amit Chantari.
I'll link to it.
But basically, he goes through and says, all right, you know Flask.
Here's what you would do if you were to convert your app or rewrite it or just learn FastAPI.
And I think a lot of people out there know the API for Flask.
So I think it'll be a quick comparison.
So the first one that we talked about a little bit is that this is API first, not page first or URL destination first.
So instead of app.route, you have app.get, app.post, and so on.
And pass the URL, of course.
That's what we talked about.
I think one that touches on some of the stuff, some of the advantages are you've got app.route.
And then you can specify a variable in there.
And in Flask, it's nice as well that you can say this variable is an integer or it is float or something.
And it'll look at that and it'll require it to be an integer.
And it will cast it to an integer and then pass it to you, right?
Yeah.
But in your framework, you just say there's a variable.
And in the function, you say it's an integer, which I think is better.
I mean, it's sort of equivalent, but it's better because the tooling, mypy, PyCharm, VS Code,
they all know that it's an integer.
Whereas the Flask version, it just happens to be.
Yeah.
So the thing is that Flask was made in the Python 2.something versions.
So it's a great syntax and some great ideas that used the available options that were there at the time.
But then the thing is that you end up having to learn this extra syntax of where to put,
like what is the type and what is the variable and what is the symbol that you have to put on the size.
There's like a little extra syntax that is specific to Flask that you have to learn.
The idea here was to use the same concepts that are like now part of Python itself.
So for example, to declare a variable, you don't use, how do you call them?
Like angular braces or like less than and more than?
Yeah, exactly.
Yeah, yeah.
Angle brackets.
Yeah.
You use call the braces, which is the same one that you use for f-strings.
So it should be relatively familiar.
Yeah.
And then to declare the type is just like in the function,
which you will probably want to declare the type in the function either way,
even if you were using Flask instead of FastAPI,
just to get like all the type support from the editor and from tools.
So.
Exactly.
That's what I do.
But you've got to have it in two places, right?
You got to have it in the route definition and in the function parameter.
And they could get out of sync, which is annoying.
I mean, this is not a huge bash on Flask type of thing.
Like you said, it just predated the concept of type annotation.
Yeah.
And actually like a lot of the things in FastAPI were learned from Flask
and from a bunch of additional plugins that were compatible with Flask,
including Marshmallow and WebArgs and Flask API spec that were great tools.
And those were my favorite tools like up until I built FastAPI to be able to have like
that functionality that they provided, but like taking the advantage of the new type of notations.
But yeah, it's like FastAPI wouldn't exist if it was not for the learnings provided by all these previous tools.
Yeah, for sure.
Another one, query strings and maybe more general, right?
Like if I understand it right, FastAPI will take the data from all the places it is provided to your endpoint, right?
So obviously if you post a JSON document or a form, it'll take it from there and put it into your argument,
but also from the query string, from the headers, from the cookies and so on, right?
Yeah. So basically the way that you declare that you, let's say in many traditional frameworks,
you receive a request, a request object, or you import in some way, you have some type of request object.
And from the request object, you extract data, you get JSON data, you get query parameters,
you get path parameters, cookies, whatever.
But you start with a request that is the same for all your functions.
In FastAPI, you can actually get the request and get the data by yourself directly if you want to do so.
So it's actually also available.
But the main point of interaction and the main point of extracting the data is not getting a request object,
but declaring the parameters in the function.
And those parameters will be extracted, validated, and everything from the request.
And also documented, because when you create an application with FastAPI,
you get automatic API documentation in a web user interface that you can explore and use interactive.
So like the main point of information extraction is in those parameters in the function.
Yeah. And what happens if it's missing, right?
Like a query string is something that may or may not be there.
Yeah. For example, let's say that you have a query string that you call like,
Yeah, well, Q because it's you are doing some query search or something.
If you declare...
Right, right. So you'd have like endpoint question mark Q equals some value you want to search for.
Yeah, exactly. Exactly.
Then you declare the function and you add a parameter that is called Q.
Because the parameter is called Q, the FastAPI will look for a parameter in the query that is also called Q.
Then you declare a type for that parameter.
Let's say it's a string because we want text string.
So FastAPI will make sure that it's a string.
Well, given it's a URL, it will always be a string.
But you could say like it's a boolean and FastAPI will make sure that it's a boolean.
Right.
But then you can also say, hey, this thing is optional.
So by default, this will be none.
So that means FastAPI, like in the Python world with like independent of FastAPI, when you declare a function that has a default value, then that parameter automatically is not required.
So FastAPI will use that same logic and make the parameter not required because it has a default value.
You could use that, for example, to say like, I want to get, I don't know, the users and I want to limit the amount of users that I want to receive by 200 and I want to skip the first thing or something like that.
You could have default values for those query parameters and say like that the default value is to skip zero because you want to get all the users from the first one.
And the limit is by default 10.
Right.
But then you will get those values in your code, but the user provides them in the query parameters, then FastAPI will read that from those query parameters.
So you actually use the same ideas that you use in normal functions to declare when something is required or not.
Like this is the basic case.
There's an additional case where you want to do additional data validation of those specific query parameters on those specific data that you're receiving.
For example, you want to make sure that the query is not longer than 100 characters or something like that.
You can do additional validation for that with another technique.
But in the basic case, it's just like the same way you use for standard functions.
Right.
Like if you wanted more validation, you could have a search parameters, Pydantic type, could have a field queue, and then you could put the Pydantic requirements and restrictions there.
Right.
Yeah.
And actually, like FastAPI will, if you declare a parameter that has a simple type like a string or a boolean or something like that, FastAPI will try to read it from the query.
But if you want to read it from, let's say, from a cookie, then you can import another utility from FastAPI and make the default value of that parameter.
That thing that you imported, which is a function, and you just call it, and that tells FastAPI, hey, I want this thing to be retrieved from the cookie, not from the query that will be the default.
Right, right.
That's a really interesting angle as well.
So because my stuff is in Pyramid, and a lot of it predates or is like right on the verge of predating the type annotations as well, certainly the framework is it gets this request and like you've got to go.
Yeah.
What's really annoying about when you're giving the request is it's not just like here's the data.
It's like, no, no, no.
There's a JSON body thing you've got to go check.
There's a form you've got to check.
There's the query string you've got to check.
There's the headers.
There's all these different dictionaries you've got to go check, right?
So what I've done so far is I've just I've set up some sort of lower level infrastructure that like, we'll just take that and merge it all into one giant dictionary and go, here's the data you've been provided.
You can ask for it.
You know, the form has or the URL has higher priority than the form, which has higher priority than like, you know, query string and all those kinds of things.
Right.
Yeah.
But yeah, this is nicer.
I'm glad you like it.
Yeah, for sure.
But yeah, it's actually it's one of those things that everyone ends up having a taste.
And sometimes people come like, hey, why are you doing it this way and not that way that I prefer?
And it's like, well, yeah, like I had to make some decisions and have some defaults.
But you can actually customize it to have whatever you actually want.
One of the things I think the API is a lot like Flask.
And I think Flask is a very popular framework.
But what's even more popular is the basic API of Flask.
Like you look at Sanic and it's kind of Flask like you look at Responder.
It's kind of Flask like, you know what I mean?
Like, yeah, this idea of I create an app and then I do app dot route or something to the equivalent.
It's just super popular.
Right.
One of the drawbacks of that API and maybe the cute little demos of like, look, you can in like 10 lines of code, write your web server.
And it's all great.
It can send people a little bit down the wrong path of like, well, now I've got this app object and I need to use it as the decorator on my stuff.
So I guess every single one of my API or view methods just goes into like the main app dot PY file.
Right.
And, you know, I think a lot of the tutorials and stuff lead people down the wrong way by just saying, well, this is just you just use app.
Right.
You just use that.
And in a real app, you would be hurting if you put that.
I mean, there's hundreds of API and for web endpoints in my apps.
And you want to put those into different modules.
Right.
Like, oh, here's where I deal with like, see your food example.
Here's how I deal with the recipes.
Here's how I go and get like a particular thing for the food.
Here's how I get like my preferences.
Like those are not all the same place.
Right.
Yeah.
So if Flask has blueprints, what does FastAPI have for the ability to factor your code into what I would call real applications?
So FastAPI has this additional tool, very similar to blueprints.
Actually, if you search for blueprint, Flask blueprints in FastAPI, you get immediately to the docs on how to do that.
There's this additional class comparable to the FastAPI class that you use to create an app that is called API router.
So you can create independent routers that are all in their specific modules, which work pretty much the same as the main app object that you will use.
But then you just have like, I don't know, router.get and the URL that you want.
And then you can import all those routers in your main application and include them in the main application.
And you can actually include them adding a prefix for the path.
So you can have like, let's say, one module, one Python file for users.
And then you have just router.get slash without even having to declare slash users slash.
You just say router.get slash.
And then it will provide you all the users.
But then at the time that you include that in the main application, you say everything that comes from this module should go with the prefix slash users slash.
Yeah, that's fantastic.
Because that's usually the thing that tells you how you want to break up your endpoints and group them, right?
Like, here's all the stuff that slash admin.
Here's all the stuff that is slash users and so on.
And you just enforce it right that way as well.
That's great.
Yeah, exactly.
Exactly.
So it's actually quite simple to do.
And you can like structure the application in a very simple way.
It's actually part of the main documentation.
Documentation is structured in a way that is a user guide and at the same time kind of reference.
But it's made in a way that you can go one point after the other and get like a full system with all the basic stuff.
And there's another section with all the extra advanced stuff.
But then you can get like learning all the bits and pieces and get to one of these things where you have an application built in with separate modules that all work together.
Yeah.
Yeah, cool.
Another thing that is common around APIs is not very common around websites, but is quite common around APIs is you need to document what are the endpoints?
What are the expected values?
Like your JSON documents look like this.
This is required.
It's an integer.
This is optional.
It's a date time and so on.
What's the story for that here?
So I guess I suffer from that maintaining corporate wikis that always got obsolete and the front end team came fighting like, hey, this thing doesn't work.
Like, no, it was updated a week ago.
So when you create an application with FastAPI, FastAPI itself is all built around open standards.
So when you create an application with FastAPI, you get an API that is based on standards and you get an open API schema.
So the standard is called open API.
And you get a schema, which is just a very big JSON that the clarion describes everything in your API.
All the paths, all the parameters, everything.
This is a big JSON that you actually kind of never look at because this JSON is read by a user interface that you can see through the web browser that documents all your API and that you can explore and actually interact with it live.
So you get automatic interactive documentation in your web browser included by default when you create an application.
And you can use it.
It's fun because sometimes people ask me, like, who is the user of that user interface?
And like, when you start this, like, you assume like no one is going to use it.
And maybe no, maybe it's just like for the final client or, but then you end up the final client ends up using it.
The front end team that you have to communicate.
And then you yourself end up using it to see like, hey, is this thing working or not?
So you actually end up using it a lot.
Exactly.
Or what does the system think?
I told it I want, right?
Does it, does it think this is optional or not optional?
I want it to be optional.
What does it say?
Yeah, exactly.
Exactly.
The thing is that because you don't have to maintain any documentation outside, or you don't have to include like additional configurations or schema inside of the, I don't know, the doc strings or things like that.
It's all generated automatically.
You don't have to do any effort to get that.
Like just by writing your standard code, you get that kind of for free.
So that's really helpful when developing and when using.
Yeah, for sure.
If I were to put a doc string that just was like a summary of what happens, would that show up in the documentation?
Yeah, exactly.
The doc string will end up in the documentation, in the interactive documentation, and you can actually use markdown and it will be rendered as a markdown.
Oh, sweet.
So yeah, it works.
As it should.
Awesome.
Yeah.
That works pretty well.
Well, and you also have like the utilities to document each of the things that you declare.
Like if you declare one of these classes that we were mentioning, you can have documentation for that specific class, which will be like a JSON payload.
It could have its own description.
Like each one of the fields could have its own description.
Like you can say name, this refers to the name of the food that you are requesting or whatever.
And it will be all there in the code and it gets all rendered in the documentation.
That's super cool.
So two final things I want to talk about.
One, I was going through the documentation and I saw that there were some optional dependencies, which is pretty interesting.
Yeah.
And so, for example, it says some are used by Pydantic, some are used by Starlet, some are used more directly.
But for example, I could optionally use micro JSON, U-J-S-O-N, but the mu, right?
The Greek micro, which is a faster implementation of JSON parsing.
And I guess if I just do pip install FastAPI, it just installs what it requires.
But maybe it uses just the built-in JSON parser, right?
Yeah, exactly.
By default, it will use the default JSON parser, which is like more standard and handles like all the cases in a more standard quote unquote way.
But if you need to have like the maximum performance, you can also install U-J-S-O-N and the TelFastAPI.
Like, hey, I want you to use not the standard JSON responses.
This U-J-S-O-N library to serialize all the data.
And actually, there in the documentation, I think in the main point where I say like, these are the optional dependencies.
There's only U-J-S-O-N is the only one mentioned.
But you can also use OR-J-S-O-N, which is equivalent to U-J-S-O-N, but is written in Rust.
And in some benchmarks is faster than U-J-S-O-N, you actually get like different ways to optimize whatever you want to do.
And to optimize like independently for each one of the things and each one of the endpoints that you need to have.
Interesting.
Yeah.
And you also, Starlette has an optional AIO files dependency, which will give you async and await capabilities for working with files.
And that plugs into like file response and static file responses and stuff like that.
Yeah, exactly.
So it's actually that Starlette is really, really lean.
Like the main code is very small and it's actually beautiful.
It's really enjoyable to read.
And it just plugs into different components and different parts for different things.
So when you use Starlette alone or FastAPI alone, for example, it won't include any ORM.
It won't include Jinja by default because you don't necessarily have to use templates.
But if you need templates, then you just import Jinja and use the templates functionality internally.
So it's made in a way that doesn't require all the possible dependencies, but you can install them and then just use them internally.
That also allows you to have like very minimal applications that are not bloated with different packages and dependencies and things.
Right.
But at the same time, you can grow very large to solve the use cases that you need to deal with.
Yeah, that's cool.
It's like a micro framework philosophy adopted there.
Yeah, exactly.
Exactly.
Last one is about hosting.
So I saw that it ships with UVicorn, which is like the ASGI Python server equivalent of G Unicorn, but based on UV loop.
Is that right?
Exactly.
That's exactly correct.
So UVicorn.
And UV loop is a high performance async and await, like event loop, basically written in C or something like that.
Yeah, and UV loop is the thing that has been providing all this high performance for all these high performance frameworks.
Like starting with Sanic, the main idea they had was let's run everything using this additional optional loop that is going to do all the asynchronous stuff.
And that's what provides all the huge performance.
So Starlette and FastAPI are frameworks, which is one of the sides of like the whole process.
Like when we were talking about this specification, this ASGI or ASGI specification, on one side you have the framework and on the other side you have the server.
UVicorn will stand on the server side.
You can use UVicorn, but you can also use another one called Hypercorn, or you can use the one that is created by the same guys from Django, which is called Daphne.
And you can use, for example, you can have UVicorn, which like starts a process using this UV loop server.
But you can also have Unicorn, the standard you will use for Flask, running a worker of UVicorn, like and running several workers.
So you can have several processes that all have like this massive high concurrency serving your application, all handled by the same unicorn as you will normally do.
Which is probably what you want to do in a production, right?
You very often have five or 10 worker processes that are controlled by G-Unicorn or Microwisgee, and then they clone themselves for the, to get the parallelism that you really would expect.
Yeah, exactly. And that's precisely what I do with the official Docker image for FastAPI.
It just does that. It has Unicorn, it runs UVicorn, and then it runs your application, and it spawns the amount of processes depending on the amount of processes available to the node that is running that Docker container.
So you can just like create the Docker file, the basic Docker file, copy your app, and it just like, by default, is fine-tuned to work pretty well.
Yeah.
If you use Docker.
So yeah, like that will probably be like the main combination you will have.
Unicorn with UbiCorn running your application.
Cool. What if I do it wrong and I host it under straight G-Unicorn or I host it under Microwisgee, both of which I don't think support ASGI, not to my knowledge anyway.
What if I host it in something that is just a WSGI server, not a ASGI server? What happens?
It just won't work.
It doesn't work at all? Okay.
It probably just won't work. Yeah. Like if you have a server that only, like a server provider, like a software as a service provider that only supports WSGI.
But actually most of the servers nowadays support ASGI. So you can actually use these frameworks very easily with most of these software as a service platforms.
Or if you use something like, I don't know, Google Cloud Run or any of these systems, there's actually a plugin, like an additional package that allows you to build FastAPI applications and run them as, what is the name?
These function services like Lambdas, these serverless things.
Right, right, right.
So you don't have to have a server running all the time. You can also like use these same tools, same ideas and run them in one of these function providers.
Oh, that's interesting. Yeah. Yeah, yeah. Cool. All right. Well, very cool project. Thanks for doing a deep dive into it.
Because I definitely think this is one of the shining examples of these new flowers and this next generation set of web frameworks that we talked about. So good job.
Thank you very much for the invitation. It's been really great.
Yeah, absolutely. Now, before we get out of here, though, I got to ask you the final two questions.
Sure.
If you're going to work on FastAPI, what editor do you use these days?
So I'm currently using Visual Studio Code, and they recently launched the new plugin for Python, which is called PyLens.
And it's really, really good. Yeah, that's my main editor currently.
Nice. And PyLens is a new language. Basically, it's the new Python plugin or built atop the other one, but it has a much better intelligence and visualization or understanding of like your whole project and like the help strings and the type information and all that kind of stuff. Right.
So it's kind of levels up the autocomplete and the tooltips and whatnot.
Exactly, because I was a complete and some of these things were pretty good with the previous plugin in Visual Studio Code.
But this one uses another tool. It's actually all written in TypeScript.
But the thing is that it provides very good autocompletion type checks, and it has like a detection of dead code.
For example, if there's something, some region in your code that will never be executed, if you're checking like if false or something like that, it will detect that this section will never be executed.
It also has a support, like it detects types very, very well.
There are some advanced ways that you can declare type annotations and that many or at least the previous version of this editor and some other editors were not able to catch it very well.
For example, when you use path leave paths, the new plugin get them very well, even when you do like sophisticated operations, including strings and things like that.
So it actually works pretty, pretty well.
Very cool.
Yeah. And I said that's quite new.
And then notable PyPI package.
Obviously, there's pip install FastAPI.
But, you know, some package you've come across lately that you're like, oh, you should definitely know about this.
So I sort, which is actually a command line package is one of the packages to do linting and recently got a new release that I sort is for doing automatic sorting of the imports in a file.
Yeah.
So you're going to, I can be very sloppy in my code and just import anywhere I'm writing the code right on top of the function.
I just import it and then I make it sort all the imports.
So it's very nice and keeps the code very tidy.
So that combined with black and other tools, it's great.
But I think iSort deserves a lot more attention than it's getting right now.
Very cool.
Yeah, that's a nice one.
All right.
Final call to action.
People are interested in FastAPI.
They want to get started.
You know, how can they bring this into their maybe existing API apps or create new APIs with it?
What do you tell them?
So I think the first thing is that I guess many people are like, hey, this seems kind of cool.
I'm not sure if I'm into it, but maybe the first thing will be just to read the main page of the documentation and try the examples there.
It's like, I don't know, it will take about 10, 15 minutes or something.
And that's enough to know if you like it or not.
That will be the main thing.
Like just the main page is enough to know if you like the thing or not.
If you don't like it then you won't like it later.
And then the other thing is that some people, including me, get very excited about new frameworks and new tools and jump right ahead to refactor everything with the new shiny tool that we found or something.
I have to tear it all down and rebuild it with this.
Absolutely.
Yeah.
We rebuilt it from scratch.
Yeah.
And it's normally not a very good idea because you have some tool that is already tested and it is already working.
And if this new thing is not really providing a very huge benefit to the thing that is already there working, it's probably not the best idea.
What many teams do and what I actually have done in many occasions is to add a layer of FastAPI on top of the current application, be it like, I don't know, Django REST framework, Flask or whatever.
And then add the new features in this FastAPI application.
And whenever I have to refactor something from the old application, then I just move it to FastAPI.
So and then I just refactor it.
But the thing is that FastAPI doesn't have like any compromise with database, with the specific tools that you use for security or for session management or for anything.
It's actually compatible with all the other tools and it's compatible with the database that you're already having and with the applications that you're already having.
So you can perfectly have both running at the same time and then just separate, let's say, by the path that they are hunting.
So everything that goes to API slash version one goes to Flask and API slash version two goes to the new FastAPI application.
I think that's a very good strategy that should work for many people when they want to add stuff.
Cool. And the mechanics of that might be in Nginx.
Instead of just saying pass everything to this URL, you can say pass everything to slash V1 slash to here, to this port or this socket and then the other one over there and kind of behind the scenes.
But at the front end, nobody knows.
Yeah, exactly.
You will do that with Nginx or Traffic.
I really like Traffic and I use it for a bunch of things.
It's also like has like automatic support for HTTPS certificates with Let's Encrypt.
And it just keeps them, makes sure that it's renewing them.
So like it's a very nice system.
And you can very easily say like, yeah, everything that goes to that specific path goes to one side and it'll be all the other.
Nginx, there's also like HAProxy.
And there's a bunch of others that can do the job.
And it's actually not that difficult to set up.
And it will probably save you like a lot of headaches with deadlines that are not fulfilled.
Right.
And if you don't like it, you just switch it back.
Yeah, exactly.
Exactly.
Yeah.
Cool.
Nevertheless, I have seen a bunch of teams that have been migrating the whole thing to FastAPI.
Unfortunately, they have been happy about it.
But yeah, I wouldn't suggest that as the main approach.
Sure.
Cool.
I guess it depends on the size of the project as well.
Yeah, of course.
Of course.
Yeah, for sure.
This has been another episode of Talk Python to Me.
Our guest on this episode was Sebastian Ramirez.
And it's been brought to you by Linode and Talk Python Training.
Start your next Python project on Linode's state-of-the-art cloud service.
Just visit talkpython.fm/Linode, L-I-N-O-D-E.
You'll automatically get a $20 credit when you create a new account.
Want to level up your Python?
If you're just getting started, try my Python Jumpstart by Building 10 Apps course.
Or if you're looking for something more advanced, check out our new Async course.
That digs into all the different types of async programming you can do in Python.
And of course, if you're interested in more than one of these, be sure to check out our
Everything Bundle.
It's like a subscription that never expires.
Be sure to subscribe to the show.
Open your favorite podcatcher and search for Python.
We should be right at the top.
You can also find the iTunes feed at /itunes.
The Google Play feed at /play.
And the direct RSS feed at /rss on talkpython.fm.
This is your host, Michael Kennedy.
Thanks so much for listening.
I really appreciate it.
Now get out there and write some Python code.
Thank you.