Transcript
This transcript was autogenerated. To make changes, submit a PR.
Hello, this is an intro to FastapI. So fast API
highperformance, easy to learn, fast to code, ready for production.
First I am Sebastin Ramrez. I'm a
developer at explosion in Kotlin,
Germany. I'm actually from Colombia, in South America. That's why
the accent. You can find me on GitHub, LinkedIn,
Twitter. I work for explosion.
Explosion are the creators of Spacey, the natural language processing
toolkit for doing things like machine learning
over text. Also the creators of
prodigy, the data set annotation tool
using active learning so you can annotate data sets
for machine learning with the model being live retrained
live with the annotations you create and sync
the deep learning framework based on standard Python
type annotations. I created Fast API and Typer.
We are going to talk about Fast API, so a little
bit about Fast API. It has currently 15,000
GitHub stars have been growing about 1000 stars per month. So people
have been liking it. It's currently being used by
Microsoft, Uber, Netflix and a bunch of other organizations
and application systems. The highperformance is in the top
rank for Python, and Fast API
is based on standards like open API, JSOn,
schema of two os. Two is what is used by Facebook,
Google, GitHub, Twitter. Whenever you do a login
with application x, all that is using Oauth
two and FastAPI lets you build these all two things very
easily or integrate with them. And because it's based on all these
open standards, it's very easy to have things like automatic API
documentation. This automatic API documentation
user interface that you can see on the right. This comes by default with
Fast API for free when you create an application using
FastAPI. So when you
create an application using FastAPI, it's based on standards for
free. You don't have to do anything to get standards
based application just because you wrote it with
fast API. Now, fast API is based on Python
type hints or type annotations. This is the
standard way you declare types in modern Python. For example, here we're saying
that name is a string or that price is a float.
This is a standard python. This is not a custom syntax, this is just
standard Python. And because this is the standard way to
declare type annotations, we get autocompletion everywhere in the
editor and type checks and type errors. We are going to see more examples of
this later. The point
is, these standard type annotations are what provides autocompletion
in your editor. So when you write code with fast API,
you get autocompletion everywhere in your code.
Fast API is easy and short. So this code
that you see here is a complete valid fast API
application. We import from Fastapi, the class Fastapi.
We create an object of this class fast API
and then we use this decorator here. A decorator is
something that uses this add symbol and what it does
is it's using in some way the
function that is below. That's what a decorator does with this add symbol.
What we are doing is we're saying in our application,
in our past API application, we want to handle the path.
Whenever we get a request that has a
URL with just the path and
is using HTTP get operation or
an HTTP get method,
we want this function to handle that. And what
we are doing is just returning a dictionary that has a message hello
world. You can see that we don't have to return
like some type of request or some type
of class or anything like that.
We just return a simple dictionary. Fast API will take care of converting this to
JsON.
So the more fast API code you write, the less
API code you actually write because fast API gets but
of the way. And then you can just focus on your business
logic for your application.
Now let's see a basic fastapi application.
This is more or less the same we just saw before we had
async def for this function. Now we have this
normal def function. If you don't know how async and wait
works or you don't feel comfortable with them, you can just use normal
functions and fast API will do the right thing for you.
So this is just the normal thing we were seeing before.
Now let's add another path here.
This is item and we are going to take this
item id as a oauth parameter. We put it in curly
braces as if it was a python form f string,
like a formatted string. We just don't add the f here,
but it's more or less the same syntax. And we declare
this item Id as a path parameter here. And we also declare it here to
receive it in our function. And we are saying that this item Id is an
integer. We are also declaring this
queue which is a string that by default is none.
Because this queue is not in the path here,
fast API will take it from the query parameters.
So if our application lives at this domain sundomain.com,
then when we send a request
with items five, this will be handled
by this function. This slash items is
the same items and this five here will be the
item id that we declare here that we are going to receive here
as a path parameter and then we can declare the
query parameter after the question mark here. So this queue equals
some query. Whenever our code executes,
this queue will have the value sum query. If we send the URL
like this, and if we check
the URL,
docsfacity eight automatically generates this documentation interface
for us. And we can see we have the slash items item id. We have
this item Id here which is the same here, that is our required
path parameter and it has to be an integer. We also have the
q query parameter which is a string and has to be
on the query. And this documentation interface is
generated automatically by fast API for
us because it's based on all these standards
that we were talking about. So whenever you write an
application with fast API, you just write some basic code.
And good guy, fast API makes a docs
user interface for your API.
Now let's check a basic Fastapi app with a body.
We are declaring here that we have a
food class and we are using this
base code thing. This is imported from pydantic. So fast API
is actually based uses internally
this library called pydantic that helps us do a
lot of all the data handling in fastapI.
And pydantic uses the standard type
annotations for Python. So we are declaring that we want a name to be
a string and we want ingredients to be this thing list. This list
is imported from typing, which is standard Python.
This typing comes from Python and this list
is the standard way to declare something as a
list in the type annotations.
So we are saying that this ingredients is a list that should contain strings
and by default it's just an empty list.
Then in the code that we have here below
we are saying that this goes to put slash.
Now this is not a get request but a post HTTP
operation and we are going to handle post
requests and we declare this parameter food
to be an instance of this
type food class that we declared before.
This is also standard Python type annotations. We have lowercase
food, this is a parameter, and this title case food is the same
class we declared here. And by doing this
we are telling Python that this food is an
instance of this class. This is just like standard Python and fast
API will use it to document
our web API as
following. So we can see that if we go to
the documentation user interface, we can see the example value
that we expect to receive in the value of
the request. And we are going to receive a name that should be a
string and we are going to receive ingredients that should be a list with
strings and this is done
automatically by fast API for us because we are using
this standard let's go back. This is standard type annotations
and this class created using
pydantic. Now let's go forward.
We can actually execute queries,
execute requests to our API. If we go
back to the quantation as we were seeing it before, we click
here and try it out and we will see that this converts to an
interactive system that we can execute and
send data to our live application. We are sending
some food that has a name of taco ingredients is frigules,
carnet, taco Shell, Savasura. So this is all valid data. We are
sending that and then we receive the message preparing taco.
This is the same code that we were returning. We are just
receiving the right response from our application.
If we go back to the code we can see that we are sending
this message that is preparing food
name and because we are actually receiving an instance we are not receiving a dictionary,
we are receiving an instance of this class food. We can use food
name to get the name that we received there. And this
is in query braces because this is an f string which is the modern
way in python to write formatted strings.
So let's go forward. We are receiving the messages that
we were expecting to receive when we
interact with the user interface for the
documentation of our API. So well,
this is how a request body full of tacos might look
like. Now let's say that we
want to declare a fast cb application that takes a body and a query
parameter. So we declare the pydantic model
as we were doing before, and now we
declare it here. This is all the same code, but we are also declaring this
delivery which is a boolean by default false because
this delivery is not part of the path, so it's
not a path parameter and because this is a simple value,
a boolean, a simple value like a string, a float.
A boolean is not a complex class like this food that
we created here. Then fast API will read it from the query parameters
also fast API we are declaring this as being a boolean.
Fast API will take care of giving us a boolean, not a string
with the word false or something like that. We will receive a boolean and
if we see it in the interactive documentation
user interface, we can see that fast API will read
that delivery query parameter that we created and we'll put
it here. We'll say that this is a boolean and it should go in the
query parameter because we declare it as a boolean. Then the user interface is
going to be able to show us the valid values which are just true or
false. And this is how we will call it if we were using
it, if we were sending a request to that. So we will declare after the
question mark we say delivery equals true. Now notice that this
URL is just a long string and this
true would actually descend as if it was a string
containing the text true. But because we
declare it in our code, let's go back. We declare it in our code
as being a boolean. Then fast API using pydantic
will take care of converting this
boolean that came as a string to an actual boolean.
So we are going to receive a boolean in our code. This is going to
be false like this one, or true if we send it
to be true. So by using
standard Python type annotations like this or this
fast API is one side doing documentation
for application automatically, and on the other side data
serialization or data conversion, we are receiving the boolean instead
of a string saying true or something
like that. And you don't have to
put everything in the body. You can use query parameters if you need to.
And there's also similar ways to declare
headers, cookies and so on.
Now let's say that we want to receive a very complex data
shape. We want to receive a JSON array
of objects in our body. So adjacent array is the equivalent of
a python list, adjacent object is the equivalent
of a python dictionary. So we are declaring here
the same way we're using this list before, we are also going to
use it here. And this list comes from the standards Python typing
module. And we are saying that we are going to receive others,
which is a list of food. And food is this pyramidic class we
created here that has name and has ingredients.
And now the code that we are doing here is very simple. It's just like
some sample small application that is collecting all the ingredients and
putting them together. So we create a list of ingredients and for each one of
the food in orders. Orders is this thing we declared here.
We are iterating for each one of the ingredients in the food
ingredients. So the ingredients for this specific food,
and we are adding that ingredient in lowercase to all
the ingredients that we have and we just return those ingredients. So we are just
collecting all the ingredients for the food orders that
we received. Now if we go to the documentation
user interface, we can see that it detects
that we declare that this should be a list of
this data shape. And we can see this is a list because we have the
square braces here. This is like
the example value that we will be sending.
So if we want to send JSON and we want to send it
inside of JSON, some JSON objects inside of JSON objects or
lists or wherever, we can do anything that we want.
We can declare very deeply complex,
deeply nested data structures and fast API will be able to
handle it all based on standard python type annotations.
But up to now we have seen that the
whole thing is doing documentation and serialization or data conversion for
us. But what happens if we send invalid data?
So let's see this one.
These are square brackets. So this is a list and we declared that
we're going to receive a list that's valid. The first item is
this JSON object and
these are all JSON objects. So that's up to now. That's valid.
The first item has a name which is a string. That's valid
because that's what we declared. And we have ingredients, which is a list of strings.
In this case click holes, cardinal, taco Shell and Sabrosura.
So this is valid. Then we have the
next one which has a name, which is pizza and
this is a string. So this is valid. We don't have ingredients here,
but if you remember the ingredients was
by default an empty list because it had a default
value then it is not required. So this is actually
a valid food item.
Now we have the next one. This has a name,
Trojan Food. This sounds weird, but up to now
this is valid because this has a name and this is a string. Now we
check the ingredients and the first item is salt. The salt
is a string inside of the list. So this is valid up to
now, but the next one is actually a JSON object
that has a name and it says that it's a sneaky
object. We declare that we are going to receive strings, not objects.
So this is weird here. And the next one has
this food name which is an old
breath. But if we check what we declare
that we are receiving is name not food name.
A name was required and because we are not sending a
name here then we are not sending a required value. And if
we try to execute this then we're going to receive an error
automatically and the error is going to tell us that we have invalid
data. So fast API is going to validate all the data based
on those type annotations automatically for us and
not just will give us an error saying like, hey, the data is invalid.
It will tell us exactly where the data is invalid.
So here we have the details. This is a list of errors and
we have two errors. The first one says that in
the body, in others the index two. So let's go back.
This is the index one, sorry, this is the index zero. This is the index
one, and this is the index two. So somewhere in here we have
an error. Let's go forward.
Inside of that index two, inside of ingredients.
In index one, a string
type has expected. So let's go back. So we
were in this object, inside of ingredients,
this is index zero. In index one, which is
this one, a string type was expected and we sent
an object. So we have a very clear specification
of where the problem is. Let's check the next
error. This says that it's in index three.
At name, the field was required and we didn't send
it. Let's go back. This is the index three
and we have a full name, but we don't have a name and the required
key that we were expecting was name. So fat
API is giving us this validation error
automatically and is doing all that for us.
Let's go back to our code. It's way
back here. That means that by using
these standard Python type annotations, fast API
is doing documentation serialization or data conversion
and validation. This is saving us like,
I don't know, 50 lines of code in here, something like
that. So whenever this code is executed, this code that we
see all here, it's because the data
is already valid. Facilities already made sure that
we have valid data and we can just focus on the business logic
of your application, not on handling specific errors
of the data shapes that you received. Now let's
go forward to where we were.
So we received this nice validation
error when we sent invalid data. So if we
send invalid data fast API or application
fast API will make that face and say, no, that's not allowed.
Now, because we are doing all this based on type annotations,
on standard Python type annotations or type hints,
we get autocompletion everywhere. Now check this.
These orders is a list of instances
of this class food. So we can iterate for each one of
the food in orders, and when we can iterate for each ingredient
in food ingredients. So you can see we are
in two for tools inside. So we are two for loops
deep. And still we get autocompletion for this
ingredient. So the editor will know that this ingredient is a string and
we will be able to have completion for this all
this because it's based on standard Python types.
This will be very difficult to achieve with other
tools and even other languages without using the standard
Python type definitions or the standard type
definitions for each specific language. So we are getting all this
for free and we are also getting type
checks. So the code, the editor is going
to know that this ingredient is a string. And if we try to sum
a string with some integer it's going to give us a nice error saying
like hey, this is not a valid operation here
and we're going to see this error early while we are developing our
application before having to wait to run the application to see
the error. Like we're going to get it right away in our editor.
All because it's all based on standard Python types.
Well yeah, this is all based on Python types. So we
get all these things for free. Now let's
say that we want to add some extra metadata. So we have this delivery query
parameter here. There is a boolean. We want to add a description.
We want to say this is pack for delivery. So from fast API we
import this query thing and then we use it here.
And before we had that the default value of delivery
was false. But now we are using, instead of putting false we are using
this query thing here. And because
we are using this space to use this query here,
we no longer have the default value here.
So this query will take as the first parameter what
that default value has before. So we are going to take this
false here. This false means that this delivery will have a default value of
false and we'd also have this additional metadata,
this description park for delivery. And then if
we check the documentation user interface we can see that
we have the park for delivery description here for this delivery
query parameter that should be a boolean and that by
default is false. So now let's go back. We now
take the false instead of having just false here, instead having
just a simple false here we have this query thing with the description and
the default value for this query parameter is going to be the first parameter of
query forward
to the documentation. Nice. We get that.
So you now don't have to write documentation
outside of your code. You don't have to keep updating
some wiki somewhere with what is the API
expecting to do. And the front end team that
is using your API is not going to come complaining that the API is no
longer working because you updated it and forgot to update the wiki
because everything will live right there in the code
right where it belongs describing what it should be describing.
Now let's say that we want to have our required query parameters,
so we have the delivery which is not required because
it has a default value of false and we now add another
one that is quantity because it doesn't have any default value
here it's going to be required and
it's going to expect an integer. So this
has to be an integer and it's a required query parameter.
If we check the documentation we can see that we have quantity here.
It's a required query parameter that has to be an integer.
If we try to execute this request here
without sending a quantity, we're going to receive this error. And if
we send it through code or from the
command line, we will also get like one of those validation errors that we were
seeing before. So this is
the has that the API will make when we omit required
data.
Now let's say that we send valid data, so we are going to
but slash and we're saying after the question mark we say the quantity is going
to be equal to two. We declare that this had to be an integer and
we are sending two. If we execute this request,
then we are going to see the response that we were expecting.
So it will work nicely as we want
something to have in mind. One does not simply make all query
parameters required because it's not
very common for query parameters to be required and clients
and or developers won't probably expect the query parameters to
be required. So if you have a query parameter that is required,
then just try to limit the amount of them. But if
you need to make them required, you can do it as well.
Now let's say that we want to add some extra validation for this
quantity query parameter. We can use the same
query thing that we were using before. We are going to use it here
as well. And we say that this quantity,
which is an integer has to be greater than zero and it has to
be less than or equal to ten. So this
has to be a value between zero and ten. And now notice that
we now are using this equals something. So this
function parameter has like quote unquote, a default
value of whatever this query thing
is here. But we wanted this quantity to be
a required query parameter. So the same way that
the one before we were using the same way that the first
parameter is the actual default value. In the case of
delivery, this was false. In the case of quantity we
also pass as the first parameter we pass
the default value. And in this case we are using three dots.
These three dots is a valid python syntax.
This is a valid python symbol called ellipsis.
And this symbol is what we are using here to tell
fast API hey, I want to use these extra
things, these nice features from this query thing, but I want
this quantity to not have a default value and still be
required. So this is how we do it. This is actually also used
by pydantic and it's actually underneath it's used using pydantic.
But this is the way that we are saying that this quantity must be required.
And now if we go to the documentation, we can see that the quantity
is required. It's an integer. And if we send invalid data,
we are going to receive one of these nice messages saying hey,
ensure this value is greater than zero because we declared that it
had to be greater than zero.
So when we say invalid data, fast API
will complain to the client and won't let the data and the code pass,
won't let the data pass and won't let the code even be executed
because it will just show the invalid data
like the invalid data errors. But if we send
valid data, then it will be all happy and work nicely
as we expected. So it's protecting us from invalid data,
from having to do all that handling in our internal
code. Now, about performance.
Fast API is in the top rank for python frameworks
in performance. The blue ones here, this is a third party
benchmark created by Tekkenpower.
And the blue ones here are Python. The green ones
are go or go lang. Go is a
compiled language, so this is not a fair comparison
at all because Python is interpreted, go is compiled and
it should be way faster than any integrated language.
But fast API
and some others here are able of being even faster than
some goal frameworks for APIs.
So this is quite nice. Which one
of these is exactly the fastest is not necessarily
that relevant. You can see that they are on the same ballpark.
And in fact, fast API here appears
on top of ubicorn. But Ubicorn is actually the bare bones server
that is running fast API. So ubiquitous, should be faster than any of
these guys, and fast API is actually built on top of starlet.
The key point is that you get some of the
best performance available
for Python frameworks, and you can see that we have
here in this benchmark. It is handling 15,000
something requests per second, so it's handling
over 9000 requests.
Cool. Some other features. Fast API has a dependency injection system
which is very simple to use, but allows you to do
very fancy stuff like authentication,
authorization, or even database session management.
A bunch of things that you can do using the dependency
injection system, and the dependency injection system is also
integrated with all these standards things, so you
also get documentation automatically with it.
It's very simple to use and very intuitive
and powerful at the same time. There are also several
security utilities integrated and integrated with
OAuth two, which is also integrated with the documentation interface.
You will have support for websockets, for files,
background tasks, easy graphql integrated templates,
like a bunch of other things. What you will expect from most
of the frameworks now when is not
a good idea to use fast API let's say that you have a stable Django
application or some other framework, and it's stable
if it's stable, if you don't need to add new features, it's working perfectly
and it's not even an API, then you don't need
to order and don't let your compulsion to refactor to rewrite
it using fast API. But something that
can be done if you want to use fast API is for example,
adding a layer of fast API on top of your existing
application to handle the new features or new
requests for some additional paths
that you declare or something like that.
That will be like a nice way to integrated it, or to slowly
migrate to fast API if you want to,
just to avoid trying to rewrite everything from scratch and
delaying your projects development. Nevertheless, there are teams
that have just integrated entirely to fast API and
have been very happy with it. But yeah, just make
sure that it actually makes sense before just going on
with fast API. Now some other tools typer
typer is like the fast API of command lined interfaces.
So this is to create command bind applications
that are running terminal. It's also based on
Python type annotations. The way you write code, it is almost
the same as with fast API. So you get completion type checks and
everything. And the nice thing is that your users,
your clients are going to get shell autocompletion in their
terminal. For bash fish Seashell
Powershell think
is the functional deep learning framework
with python type annotations that is compatible with your favorite
libraries like Tensorflow or Pytorch. This is built by
explosion, the company I work for. And if you like these Python
type annotations and you are into deep learning, you might
want to check think as well.
Thank you very much. That's what I have for you.
If you want to check, the documentation is here. Fast API
here's the documentation for FastapI. If you have any
questions, you can ask them on the issues in the
GitHub repository or you can also contact
me in these links.
Thank you very much.