0% found this document useful (0 votes)
71 views381 pages

Fastapi Tiangolo Com Learn-Combined

This document serves as an introductory guide to learning FastAPI, emphasizing the importance of Python type hints for better code support and error checking. It covers the basics of type hints, including their syntax, usage with various data types, and integration with FastAPI and Pydantic for data validation. The document also highlights the advantages of using type hints in Python, particularly in enhancing developer experience and code quality.

Uploaded by

umarbellokanwa
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
71 views381 pages

Fastapi Tiangolo Com Learn-Combined

This document serves as an introductory guide to learning FastAPI, emphasizing the importance of Python type hints for better code support and error checking. It covers the basics of type hints, including their syntax, usage with various data types, and integration with FastAPI and Pydantic for data validation. The document also highlights the advantages of using type hints in Python, particularly in enhancing developer experience and code quality.

Uploaded by

umarbellokanwa
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 381

Learn

Here are the introductory sections and the tutorials to learn FastAPI.

You could consider this a book, a course, the official and recommended way to learn FastAPI. 😎

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Python Types Intro

Python has support for optional "type hints" (also called "type annotations").

These "type hints" or annotations are a special syntax that allow declaring the type of a variable.

By declaring types for your variables, editors and tools can give you better support.

This is just a quick tutorial / refresher about Python type hints. It covers only the minimum necessary to use them
with FastAPI... which is actually very little.

FastAPI is all based on these type hints, they give it many advantages and benefits.

But even if you never use FastAPI, you would benefit from learning a bit about them.

Note

If you are a Python expert, and you already know everything about type hints, skip to the next chapter.

Motivation
Let's start with a simple example:

def get_full_name(first_name, last_name):


full_name = first_name.title() + " " + last_name.title()
return full_name

print(get_full_name("john", "doe"))

Calling this program outputs:

John Doe

The function does the following:

Takes a first_name and last_name .

Converts the first letter of each one to upper case with title() .

Concatenates them with a space in the middle.

def get_full_name(first_name, last_name):


full_name = first_name.title() + " " + last_name.title()
return full_name

print(get_full_name("john", "doe"))

Edit it

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
It's a very simple program.

But now imagine that you were writing it from scratch.

At some point you would have started the definition of the function, you had the parameters ready...

But then you have to call "that method that converts the first letter to upper case".

Was it upper ? Was it uppercase ? first_uppercase ? capitalize ?

Then, you try with the old programmer's friend, editor autocompletion.

You type the first parameter of the function, first_name , then a dot ( . ) and then hit Ctrl+Space to trigger the
completion.

But, sadly, you get nothing useful:

Add types

Let's modify a single line from the previous version.

We will change exactly this fragment, the parameters of the function, from:

first_name, last_name

to:

first_name: str, last_name: str

That's it.

Those are the "type hints":

def get_full_name(first_name: str, last_name: str):


full_name = first_name.title() + " " + last_name.title()
return full_name

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
print(get_full_name("john", "doe"))

That is not the same as declaring default values like would be with:

first_name="john", last_name="doe"

It's a different thing.

We are using colons ( : ), not equals ( = ).

And adding type hints normally doesn't change what happens from what would happen without them.

But now, imagine you are again in the middle of creating that function, but with type hints.

At the same point, you try to trigger the autocomplete with Ctrl+Space and you see:

With that, you can scroll, seeing the options, until you find the one that "rings a bell":

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
More motivation
Check this function, it already has type hints:

def get_name_with_age(name: str, age: int):


name_with_age = name + " is this old: " + age
return name_with_age

Because the editor knows the types of the variables, you don't only get completion, you also get error checks:

Now you know that you have to fix it, convert age to a string with str(age) :

def get_name_with_age(name: str, age: int):


name_with_age = name + " is this old: " + str(age)
return name_with_age

Declaring types
You just saw the main place to declare type hints. As function parameters.

This is also the main place you would use them with FastAPI.

Simple types

You can declare all the standard Python types, not only str .

You can use, for example:

int

float

bool

bytes

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
def get_items(item_a: str, item_b: int, item_c: float, item_d: bool, item_e: bytes):
return item_a, item_b, item_c, item_d, item_d, item_e

Generic types with type parameters

There are some data structures that can contain other values, like dict , list , set and tuple . And the internal
values can have their own type too.

These types that have internal types are called "generic" types. And it's possible to declare them, even with their
internal types.

To declare those types and the internal types, you can use the standard Python module typing . It exists specifically
to support these type hints.

Newer versions of Python

The syntax using typing is compatible with all versions, from Python 3.6 to the latest ones, including Python 3.9,
Python 3.10, etc.

As Python advances, newer versions come with improved support for these type annotations and in many cases you
won't even need to import and use the typing module to declare the type annotations.

If you can choose a more recent version of Python for your project, you will be able to take advantage of that extra
simplicity.

In all the docs there are examples compatible with each version of Python (when there's a difference).

For example "Python 3.6+" means it's compatible with Python 3.6 or above (including 3.7, 3.8, 3.9, 3.10, etc). And
"Python 3.9+" means it's compatible with Python 3.9 or above (including 3.10, etc).

If you can use the latest versions of Python, use the examples for the latest version, those will have the best and
simplest syntax, for example, "Python 3.10+".

List

For example, let's define a variable to be a list of str .

Python 3.9+ Python 3.8+

Declare the variable, with the same colon ( : ) syntax.

As the type, put list .

As the list is a type that contains some internal types, you put them in square brackets:

def process_items(items: list[str]):


for item in items:
print(item)

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Info

Those internal types in the square brackets are called "type parameters".

In this case, str is the type parameter passed to List (or list in Python 3.9 and above).

That means: "the variable items is a list , and each of the items in this list is a str ".

Tip

If you use Python 3.9 or above, you don't have to import List from typing , you can use the same regular list type instead.

By doing that, your editor can provide support even while processing items from the list:

Without types, that's almost impossible to achieve.

Notice that the variable item is one of the elements in the list items .

And still, the editor knows it is a str , and provides support for that.

Tuple and Set

You would do the same to declare tuple s and set s:

Python 3.9+ Python 3.8+

def process_items(items_t: tuple[int, int, str], items_s: set[bytes]):


return items_t, items_s

This means:

The variable items_t is a tuple with 3 items, an int , another int , and a str .

The variable items_s is a set , and each of its items is of type bytes .

Dict

To define a dict , you pass 2 type parameters, separated by commas.

The first type parameter is for the keys of the dict .

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
The second type parameter is for the values of the dict :

Python 3.9+ Python 3.8+

def process_items(prices: dict[str, float]):


for item_name, item_price in prices.items():
print(item_name)
print(item_price)

This means:

The variable prices is a dict :

The keys of this dict are of type str (let's say, the name of each item).

The values of this dict are of type float (let's say, the price of each item).

Union

You can declare that a variable can be any of several types, for example, an int or a str .

In Python 3.6 and above (including Python 3.10) you can use the Union type from typing and put inside the square
brackets the possible types to accept.

In Python 3.10 there's also a new syntax where you can put the possible types separated by a vertical bar ( | ).

Python 3.10+ Python 3.8+

def process_item(item: int | str):


print(item)

In both cases this means that item could be an int or a str .

Possibly None

You can declare that a value could have a type, like str , but that it could also be None .

In Python 3.6 and above (including Python 3.10) you can declare it by importing and using Optional from the
typing module.

from typing import Optional

def say_hi(name: Optional[str] = None):


if name is not None:
print(f"Hey {name}!")
else:
print("Hello World")

Using Optional[str] instead of just str will let the editor help you detecting errors where you could be assuming
that a value is always a str , when it could actually be None too.

Optional[Something] is actually a shortcut for Union[Something, None] , they are equivalent.

This also means that in Python 3.10, you can use Something | None :

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Python 3.10+ Python 3.8+ Python 3.8+ alternative

def say_hi(name: str | None = None):


if name is not None:
print(f"Hey {name}!")
else:
print("Hello World")

Using Union or Optional

If you are using a Python version below 3.10, here's a tip from my very subjective point of view:

🚨 Avoid using Optional[SomeType]


Instead ✨ use Union[SomeType, None] ✨.

Both are equivalent and underneath they are the same, but I would recommend Union instead of Optional because
the word "optional" would seem to imply that the value is optional, and it actually means "it can be None ", even if it's
not optional and is still required.

I think Union[SomeType, None] is more explicit about what it means.

It's just about the words and names. But those words can affect how you and your teammates think about the code.

As an example, let's take this function:

from typing import Optional

def say_hi(name: Optional[str]):


print(f"Hey {name}!")

The parameter name is defined as Optional[str] , but it is not optional, you cannot call the function without the
parameter:

say_hi() # Oh, no, this throws an error! 😱


The name parameter is still required (not optional) because it doesn't have a default value. Still, name accepts None
as the value:

say_hi(name=None) # This works, None is valid 🎉


The good news is, once you are on Python 3.10 you won't have to worry about that, as you will be able to simply use
| to define unions of types:

def say_hi(name: str | None):


print(f"Hey {name}!")

And then you won't have to worry about names like Optional and Union . 😎
Generic types

These types that take type parameters in square brackets are called Generic types or Generics, for example:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Python 3.10+ Python 3.9+ Python 3.8+

You can use the same builtin types as generics (with square brackets and types inside):

list

tuple

set

dict

And the same as with Python 3.8, from the typing module:

Union

Optional (the same as with Python 3.8)

...and others.

In Python 3.10, as an alternative to using the generics Union and Optional , you can use the vertical bar ( | ) to
declare unions of types, that's a lot better and simpler.

Classes as types

You can also declare a class as the type of a variable.

Let's say you have a class Person , with a name:

class Person:
def __init__(self, name: str):
self.name = name

def get_person_name(one_person: Person):


return one_person.name

Then you can declare a variable to be of type Person :

class Person:
def __init__(self, name: str):
self.name = name

def get_person_name(one_person: Person):


return one_person.name

And then, again, you get all the editor support:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Notice that this means " one_person is an instance of the class Person ".

It doesn't mean " one_person is the class called Person ".

Pydantic models
Pydantic [↪] is a Python library to perform data validation.

You declare the "shape" of the data as classes with attributes.

And each attribute has a type.

Then you create an instance of that class with some values and it will validate the values, convert them to the
appropriate type (if that's the case) and give you an object with all the data.

And you get all the editor support with that resulting object.

An example from the official Pydantic docs:

Python 3.10+ Python 3.9+ Python 3.8+

from datetime import datetime

from pydantic import BaseModel

class User(BaseModel):
id: int
name: str = "John Doe"
signup_ts: datetime | None = None
friends: list[int] = []

external_data = {
"id": "123",
"signup_ts": "2017-06-01 12:22",
"friends": [1, "2", b"3"],

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
}
user = User(**external_data)
print(user)
# > User id=123 name='John Doe' signup_ts=datetime.datetime(2017, 6, 1, 12, 22) friends=[1, 2, 3]
print(user.id)
# > 123

Info

To learn more about Pydantic, check its docs [↪].

FastAPI is all based on Pydantic.

You will see a lot more of all this in practice in the Tutorial - User Guide ↪.

Tip

Pydantic has a special behavior when you use Optional or Union[Something, None] without a default value, you can read more about it in
the Pydantic docs about Required Optional fields [↪].

Type Hints with Metadata Annotations


Python also has a feature that allows putting additional metadata in these type hints using Annotated .

Python 3.9+ Python 3.8+

In Python 3.9, Annotated is part of the standard library, so you can import it from typing .

from typing import Annotated

def say_hello(name: Annotated[str, "this is just metadata"]) -> str:


return f"Hello {name}"

Python itself doesn't do anything with this Annotated . And for editors and other tools, the type is still str .

But you can use this space in Annotated to provide FastAPI with additional metadata about how you want your
application to behave.

The important thing to remember is that the first type parameter you pass to Annotated is the actual type. The rest,
is just metadata for other tools.

For now, you just need to know that Annotated exists, and that it's standard Python. 😎
Later you will see how powerful it can be.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Tip

The fact that this is standard Python means that you will still get the best possible developer experience in your editor, with the tools you use
to analyze and refactor your code, etc. ✨
And also that your code will be very compatible with many other Python tools and libraries. 🚀

Type hints in FastAPI


FastAPI takes advantage of these type hints to do several things.

With FastAPI you declare parameters with type hints and you get:

Editor support.

Type checks.

...and FastAPI uses the same declarations to:

Define requirements: from request path parameters, query parameters, headers, bodies, dependencies, etc.

Convert data: from the request to the required type.

Validate data: coming from each request:

Generating automatic errors returned to the client when the data is invalid.

Document the API using OpenAPI:

which is then used by the automatic interactive documentation user interfaces.

This might all sound abstract. Don't worry. You'll see all this in action in the Tutorial - User Guide ↪.

The important thing is that by using standard Python types, in a single place (instead of adding more classes,
decorators, etc), FastAPI will do a lot of the work for you.

Info

If you already went through all the tutorial and came back to see more about types, a good resource is the "cheat sheet" from mypy [↪].

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Concurrency and async / await

Details about the async def syntax for path operation functions and some background about
asynchronous code, concurrency, and parallelism.

In a hurry?
TL;DR:

If you are using third party libraries that tell you to call them with await , like:

results = await some_library()

Then, declare your path operation functions with async def like:

@app.get('/')
async def read_results():
results = await some_library()
return results

Note

You can only use await inside of functions created with async def .

If you are using a third party library that communicates with something (a database, an API, the file
system, etc.) and doesn't have support for using await , (this is currently the case for most
database libraries), then declare your path operation functions as normally, with just def , like:

@app.get('/')
def results():
results = some_library()
return results

If your application (somehow) doesn't have to communicate with anything else and wait for it to
respond, use async def .

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
If you just don't know, use normal def .

Note: You can mix def and async def in your path operation functions as much as you need and
define each one using the best option for you. FastAPI will do the right thing with them.

Anyway, in any of the cases above, FastAPI will still work asynchronously and be extremely fast.

But by following the steps above, it will be able to do some performance optimizations.

Technical Details
Modern versions of Python have support for "asynchronous code" using something called
"coroutines", with async and await syntax.

Let's see that phrase by parts in the sections below:

Asynchronous Code

async and await

Coroutines

Asynchronous Code
Asynchronous code just means that the language 💬 has a way to tell the computer / program 🤖
that at some point in the code, it 🤖 will have to wait for something else to finish somewhere else.
Let's say that something else is called "slow-file" 📝.

So, during that time, the computer can go and do some other work, while "slow-file" 📝 finishes.

Then the computer / program 🤖 will come back every time it has a chance because it's waiting
again, or whenever it 🤖 finished all the work it had at that point. And it 🤖 will see if any of the tasks
it was waiting for have already finished, doing whatever it had to do.

Next, it 🤖 takes the first task to finish (let's say, our "slow-file" 📝) and continues whatever it had to
do with it.

That "wait for something else" normally refers to I/O operations that are relatively "slow" (compared
to the speed of the processor and the RAM memory), like waiting for:

the data from the client to be sent through the network

the data sent by your program to be received by the client through the network

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
the contents of a file in the disk to be read by the system and given to your program

the contents your program gave to the system to be written to disk

a remote API operation

a database operation to finish

a database query to return the results

etc.

As the execution time is consumed mostly by waiting for I/O operations, they call them "I/O bound"
operations.

It's called "asynchronous" because the computer / program doesn't have to be "synchronized" with
the slow task, waiting for the exact moment that the task finishes, while doing nothing, to be able to
take the task result and continue the work.

Instead of that, by being an "asynchronous" system, once finished, the task can wait in line a little bit
(some microseconds) for the computer / program to finish whatever it went to do, and then come
back to take the results and continue working with them.

For "synchronous" (contrary to "asynchronous") they commonly also use the term "sequential",
because the computer / program follows all the steps in sequence before switching to a different
task, even if those steps involve waiting.

Concurrency and Burgers

This idea of asynchronous code described above is also sometimes called "concurrency". It is
different from "parallelism".

Concurrency and parallelism both relate to "different things happening more or less at the same
time".

But the details between concurrency and parallelism are quite different.

To see the difference, imagine the following story about burgers:

Concurrent Burgers

You go with your crush to get fast food, you stand in line while the cashier takes the orders from the
people in front of you. 😍

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Then it's your turn, you place your order of 2 very fancy burgers for your crush and you. 🍔🍔

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
The cashier says something to the cook in the kitchen so they know they have to prepare your
burgers (even though they are currently preparing the ones for the previous clients).

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
You pay. 💸
The cashier gives you the number of your turn.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
While you are waiting, you go with your crush and pick a table, you sit and talk with your crush for a
long time (as your burgers are very fancy and take some time to prepare).

As you are sitting at the table with your crush, while you wait for the burgers, you can spend that
time admiring how awesome, cute and smart your crush is ✨😍✨.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
While waiting and talking to your crush, from time to time, you check the number displayed on the
counter to see if it's your turn already.

Then at some point, it finally is your turn. You go to the counter, get your burgers and come back to
the table.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
You and your crush eat the burgers and have a nice time. ✨

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Info

Beautiful illustrations by Ketrina Thompson [↪]. 🎨

Imagine you are the computer / program 🤖 in that story.


While you are at the line, you are just idle 😴, waiting for your turn, not doing anything very
"productive". But the line is fast because the cashier is only taking the orders (not preparing them),
so that's fine.

Then, when it's your turn, you do actual "productive" work, you process the menu, decide what you
want, get your crush's choice, pay, check that you give the correct bill or card, check that you are
charged correctly, check that the order has the correct items, etc.

But then, even though you still don't have your burgers, your work with the cashier is "on pause" ⏸,
because you have to wait 🕙 for your burgers to be ready.
But as you go away from the counter and sit at the table with a number for your turn, you can switch
🔀 your attention to your crush, and "work" ⏯ 🤓 on that. Then you are again doing something very
"productive" as is flirting with your crush 😍.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Then the cashier 💁 says "I'm finished with doing the burgers" by putting your number on the
counter's display, but you don't jump like crazy immediately when the displayed number changes to
your turn number. You know no one will steal your burgers because you have the number of your
turn, and they have theirs.

So you wait for your crush to finish the story (finish the current work ⏯ / task being processed 🤓),
smile gently and say that you are going for the burgers ⏸.

Then you go to the counter 🔀, to the initial task that is now finished ⏯, pick the burgers, say thanks
and take them to the table. That finishes that step / task of interaction with the counter ⏹. That in
turn, creates a new task, of "eating burgers" 🔀 ⏯, but the previous one of "getting burgers" is
finished ⏹.

Parallel Burgers

Now let's imagine these aren't "Concurrent Burgers", but "Parallel Burgers".

You go with your crush to get parallel fast food.

You stand in line while several (let's say 8) cashiers that at the same time are cooks take the orders
from the people in front of you.

Everyone before you is waiting for their burgers to be ready before leaving the counter because each
of the 8 cashiers goes and prepares the burger right away before getting the next order.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Then it's finally your turn, you place your order of 2 very fancy burgers for your crush and you.

You pay 💸.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
The cashier goes to the kitchen.

You wait, standing in front of the counter 🕙, so that no one else takes your burgers before you do,
as there are no numbers for turns.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
As you and your crush are busy not letting anyone get in front of you and take your burgers
whenever they arrive, you cannot pay attention to your crush. 😞
This is "synchronous" work, you are "synchronized" with the cashier/cook 👨‍🍳. You have to wait 🕙
and be there at the exact moment that the cashier/cook 👨‍🍳 finishes the burgers and gives them to
you, or otherwise, someone else might take them.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Then your cashier/cook 👨‍🍳 finally comes back with your burgers, after a long time waiting 🕙 there
in front of the counter.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
You take your burgers and go to the table with your crush.

You just eat them, and you are done. ⏹

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
There was not much talk or flirting as most of the time was spent waiting 🕙 in front of the counter.
😞
Info

Beautiful illustrations by Ketrina Thompson [↪]. 🎨

🤖 with two processors (you


In this scenario of the parallel burgers, you are a computer / program
and your crush), both waiting 🕙 and dedicating their attention ⏯ to be "waiting on the counter" 🕙
for a long time.

The fast food store has 8 processors (cashiers/cooks). While the concurrent burgers store might
have had only 2 (one cashier and one cook).

But still, the final experience is not the best. 😞


This would be the parallel equivalent story for burgers. 🍔
For a more "real life" example of this, imagine a bank.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Up to recently, most of the banks had multiple cashiers 👨‍💼👨‍💼👨‍💼👨‍💼 and a big line 🕙🕙🕙🕙🕙🕙
🕙🕙.
All of the cashiers doing all the work with one client after the other 👨‍💼⏯.
And you have to wait🕙 in the line for a long time or you lose your turn.
You probably wouldn't want to take your crush 😍 with you to run errands at the bank 🏦.

Burger Conclusion

In this scenario of "fast food burgers with your crush", as there is a lot of waiting 🕙, it makes a lot
more sense to have a concurrent system ⏸ 🔀⏯.
This is the case for most of the web applications.

Many, many users, but your server is waiting 🕙 for their not-so-good connection to send their
requests.

And then waiting 🕙 again for the responses to come back.


This "waiting" 🕙 is measured in microseconds, but still, summing it all, it's a lot of waiting in the
end.

That's why it makes a lot of sense to use asynchronous ⏸ 🔀⏯ code for web APIs.
This kind of asynchronicity is what made NodeJS popular (even though NodeJS is not parallel) and
that's the strength of Go as a programming language.

And that's the same level of performance you get with FastAPI.

And as you can have parallelism and asynchronicity at the same time, you get higher performance
than most of the tested NodeJS frameworks and on par with Go, which is a compiled language
closer to C (all thanks to Starlette) [↪].

Is concurrency better than parallelism?

Nope! That's not the moral of the story.

Concurrency is different than parallelism. And it is better on specific scenarios that involve a lot of
waiting. Because of that, it generally is a lot better than parallelism for web application development.
But not for everything.

So, to balance that out, imagine the following short story:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
You have to clean a big, dirty house.

Yep, that's the whole story.

There's no waiting 🕙 anywhere, just a lot of work to be done, on multiple places of the house.
You could have turns as in the burgers example, first the living room, then the kitchen, but as you are
not waiting 🕙 for anything, just cleaning and cleaning, the turns wouldn't affect anything.
It would take the same amount of time to finish with or without turns (concurrency) and you would
have done the same amount of work.

But in this case, if you could bring the 8 ex-cashier/cooks/now-cleaners, and each one of them (plus
you) could take a zone of the house to clean it, you could do all the work in parallel, with the extra
help, and finish much sooner.

In this scenario, each one of the cleaners (including you) would be a processor, doing their part of
the job.

And as most of the execution time is taken by actual work (instead of waiting), and the work in a
computer is done by a CPU, they call these problems "CPU bound".

Common examples of CPU bound operations are things that require complex math processing.

For example:

Audio or image processing.

Computer vision: an image is composed of millions of pixels, each pixel has 3 values / colors,
processing that normally requires computing something on those pixels, all at the same time.

Machine Learning: it normally requires lots of "matrix" and "vector" multiplications. Think of a
huge spreadsheet with numbers and multiplying all of them together at the same time.

Deep Learning: this is a sub-field of Machine Learning, so, the same applies. It's just that there is
not a single spreadsheet of numbers to multiply, but a huge set of them, and in many cases, you
use a special processor to build and / or use those models.

Concurrency + Parallelism: Web + Machine Learning

With FastAPI you can take the advantage of concurrency that is very common for web development
(the same main attraction of NodeJS).

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
But you can also exploit the benefits of parallelism and multiprocessing (having multiple processes
running in parallel) for CPU bound workloads like those in Machine Learning systems.

That, plus the simple fact that Python is the main language for Data Science, Machine Learning and
especially Deep Learning, make FastAPI a very good match for Data Science / Machine Learning
web APIs and applications (among many others).

To see how to achieve this parallelism in production see the section about Deployment ↪.

async and await


Modern versions of Python have a very intuitive way to define asynchronous code. This makes it
look just like normal "sequential" code and do the "awaiting" for you at the right moments.

When there is an operation that will require waiting before giving the results and has support for
these new Python features, you can code it like:

burgers = await get_burgers(2)

The key here is the await . It tells Python that it has to wait ⏸ for get_burgers(2) to finish doing
🕙 before storing the results in burgers . With that, Python will know that it can go and do
its thing
something else 🔀 ⏯ in the meanwhile (like receiving another request).

For await to work, it has to be inside a function that supports this asynchronicity. To do that, you
just declare it with async def :

async def get_burgers(number: int):


# Do some asynchronous stuff to create the burgers
return burgers

...instead of def :

# This is not asynchronous


def get_sequential_burgers(number: int):
# Do some sequential stuff to create the burgers
return burgers

With async def , Python knows that, inside that function, it has to be aware of await expressions,
and that it can "pause" ⏸ the execution of that function and go do something else 🔀 before coming
back.

When you want to call an async def function, you have to "await" it. So, this won't work:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
# This won't work, because get_burgers was defined with: async def
burgers = get_burgers(2)

So, if you are using a library that tells you that you can call it with await , you need to create the path
operation functions that uses it with async def , like in:

@app.get('/burgers')
async def read_burgers():
burgers = await get_burgers(2)
return burgers

More technical details

You might have noticed that await can only be used inside of functions defined with async def .

But at the same time, functions defined with async def have to be "awaited". So, functions with
async def can only be called inside of functions defined with async def too.

So, about the egg and the chicken, how do you call the first async function?

If you are working with FastAPI you don't have to worry about that, because that "first" function will
be your path operation function, and FastAPI will know how to do the right thing.

But if you want to use async / await without FastAPI, you can do it as well.

Write your own async code

Starlette (and FastAPI) are based on AnyIO [↪], which makes it compatible with both Python's
standard library asyncio [↪] and Trio [↪].

In particular, you can directly use AnyIO [↪] for your advanced concurrency use cases that require
more advanced patterns in your own code.

And even if you were not using FastAPI, you could also write your own async applications with
AnyIO [↪] to be highly compatible and get its benefits (e.g. structured concurrency).

Other forms of asynchronous code

This style of using async and await is relatively new in the language.

But it makes working with asynchronous code a lot easier.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
This same syntax (or almost identical) was also included recently in modern versions of JavaScript
(in Browser and NodeJS).

But before that, handling asynchronous code was quite more complex and difficult.

In previous versions of Python, you could have used threads or Gevent [↪]. But the code is way more
complex to understand, debug, and think about.

In previous versions of NodeJS / Browser JavaScript, you would have used "callbacks". Which leads
to callback hell [↪].

Coroutines
Coroutine is just the very fancy term for the thing returned by an async def function. Python knows
that it is something like a function that it can start and that it will end at some point, but that it might
be paused ⏸ internally too, whenever there is an await inside of it.

But all this functionality of using asynchronous code with async and await is many times
summarized as using "coroutines". It is comparable to the main key feature of Go, the "Goroutines".

Conclusion
Let's see the same phrase from above:

Modern versions of Python have support for "asynchronous code" using something called
"coroutines", with async and await syntax.

That should make more sense now. ✨


All that is what powers FastAPI (through Starlette) and what makes it have such an impressive
performance.

Very Technical Details

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Warning

You can probably skip this.

These are very technical details of how FastAPI works underneath.

If you have quite some technical knowledge (coroutines, threads, blocking, etc.) and are curious about how FastAPI
handles async def vs normal def , go ahead.

Path operation functions

When you declare a path operation function with normal def instead of async def , it is run in an
external threadpool that is then awaited, instead of being called directly (as it would block the
server).

If you are coming from another async framework that does not work in the way described above and
you are used to defining trivial compute-only path operation functions with plain def for a tiny
performance gain (about 100 nanoseconds), please note that in FastAPI the effect would be quite
opposite. In these cases, it's better to use async def unless your path operation functions use code
that performs blocking I/O.

Still, in both situations, chances are that FastAPI will still be faster ↪ than (or at least comparable
to) your previous framework.

Dependencies

The same applies for dependencies ↪. If a dependency is a standard def function instead of
async def , it is run in the external threadpool.

Sub-dependencies

You can have multiple dependencies and sub-dependencies ↪ requiring each other (as parameters
of the function definitions), some of them might be created with async def and some with normal
def . It would still work, and the ones created with normal def would be called on an external
thread (from the threadpool) instead of being "awaited".

Other utility functions

Any other utility function that you call directly can be created with normal def or async def and
FastAPI won't affect the way you call it.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
This is in contrast to the functions that FastAPI calls for you: path operation functions and
dependencies.

If your utility function is a normal function with def , it will be called directly (as you write it in your
code), not in a threadpool, if the function is created with async def then you should await for that
function when you call it in your code.

Again, these are very technical details that would probably be useful if you came searching for them.

Otherwise, you should be good with the guidelines from the section above: In a hurry?.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Tutorial - User Guide

This tutorial shows you how to use FastAPI with most of its features, step by step.

Each section gradually builds on the previous ones, but it's structured to separate topics, so that you can
go directly to any specific one to solve your specific API needs.

It is also built to work as a future reference.

So you can come back and see exactly what you need.

Run the code


All the code blocks can be copied and used directly (they are actually tested Python files).

To run any of the examples, copy the code to a file main.py , and start fastapi dev with:

bash

fast →
$ fastapi dev main.py
INFO Using path main.py
INFO Resolved absolute path /home/user/code/awesomeapp/main.py
INFO Searching for package file structure from directories with
__init__.py files
INFO Importing from /home/user/code/awesomeapp
╭─ Python module file ─╮



🐍main.py



╰──────────────────────╯

INFO Importing module main


INFO Found importable FastAPI app
╭─ Importable FastAPI app ─╮
│ │
│ from main import app │
│ │
╰──────────────────────────╯

INFO Using import string main:app


╭────────── FastAPI CLI - Development mode ───────────╮
│ │
│ Serving at: http://127.0.0.1:8000 │
│ │

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
│ API docs: http://127.0.0.1:8000/docs │
│ │
│ Running in development mode, for production use: │
│ │
│ fastapi run │
│ │
╰─────────────────────────────────────────────────────╯

INFO: Will watch for changes in these directories:


['/home/user/code/awesomeapp']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [2265862] using WatchFiles
INFO: Started server process [2265873]
INFO: Waiting for application startup.
INFO: Application startup complete.

It is HIGHLY encouraged that you write or copy the code, edit it and run it locally.

Using it in your editor is what really shows you the benefits of FastAPI, seeing how little code you have to
write, all the type checks, autocompletion, etc.

Install FastAPI
The first step is to install FastAPI:

bash

fast →
$ pip install fastapi

Note

When you install with pip install fastapi it comes with some default optional standard dependencies.

If you don't want to have those optional dependencies, you can instead install pip install fastapi-slim .

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Advanced User Guide
There is also an Advanced User Guide that you can read later after this Tutorial - User guide.

The Advanced User Guide, builds on this, uses the same concepts, and teaches you some extra features.

But you should first read the Tutorial - User Guide (what you are reading right now).

It's designed so that you can build a complete application with just the Tutorial - User Guide, and then
extend it in different ways, depending on your needs, using some of the additional ideas from the
Advanced User Guide.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
First Steps

The simplest FastAPI file could look like this:

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
return {"message": "Hello World"}

Copy that to a file main.py .

Run the live server:

bash

fast →
$ fastapi d ▋

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
In the output, there's a line with something like:

INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

That line shows the URL where your app is being served, in your local machine.

Check it

Open your browser at http://127.0.0.1:8000 [↪].

You will see the JSON response as:

{"message": "Hello World"}

Interactive API docs

Now go to http://127.0.0.1:8000/docs [↪].

You will see the automatic interactive API documentation (provided by Swagger UI [↪]):

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Alternative API docs

And now, go to http://127.0.0.1:8000/redoc [↪].

You will see the alternative automatic documentation (provided by ReDoc [↪]):

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
OpenAPI

FastAPI generates a "schema" with all your API using the OpenAPI standard for defining APIs.

"Schema"

A "schema" is a definition or description of something. Not the code that implements it, but just an abstract
description.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
API "schema"

In this case, OpenAPI [↪] is a specification that dictates how to define a schema of your API.

This schema definition includes your API paths, the possible parameters they take, etc.

Data "schema"

The term "schema" might also refer to the shape of some data, like a JSON content.

In that case, it would mean the JSON attributes, and data types they have, etc.

OpenAPI and JSON Schema

OpenAPI defines an API schema for your API. And that schema includes definitions (or "schemas") of the
data sent and received by your API using JSON Schema, the standard for JSON data schemas.

Check the openapi.json

If you are curious about how the raw OpenAPI schema looks like, FastAPI automatically generates a JSON
(schema) with the descriptions of all your API.

You can see it directly at: http://127.0.0.1:8000/openapi.json [↪].

It will show a JSON starting with something like:

{
"openapi": "3.1.0",
"info": {
"title": "FastAPI",
"version": "0.1.0"
},
"paths": {
"/items/": {
"get": {
"responses": {
"200": {
"description": "Successful Response",
"content": {
"application/json": {

...

What is OpenAPI for

The OpenAPI schema is what powers the two interactive documentation systems included.

And there are dozens of alternatives, all based on OpenAPI. You could easily add any of those alternatives
to your application built with FastAPI.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
You could also use it to generate code automatically, for clients that communicate with your API. For
example, frontend, mobile or IoT applications.

Recap, step by step


Step 1: import FastAPI

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
return {"message": "Hello World"}

FastAPI is a Python class that provides all the functionality for your API.

Technical Details

FastAPI is a class that inherits directly from Starlette .

You can use all the Starlette [↪] functionality with FastAPI too.

Step 2: create a FastAPI "instance"

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
return {"message": "Hello World"}

Here the app variable will be an "instance" of the class FastAPI .

This will be the main point of interaction to create all your API.

Step 3: create a path operation

Path

"Path" here refers to the last part of the URL starting from the first / .

So, in a URL like:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
https://example.com/items/foo

...the path would be:

/items/foo

Info

A "path" is also commonly called an "endpoint" or a "route".

While building an API, the "path" is the main way to separate "concerns" and "resources".

Operation

"Operation" here refers to one of the HTTP "methods".

One of:

POST

GET

PUT

DELETE

...and the more exotic ones:

OPTIONS

HEAD

PATCH

TRACE

In the HTTP protocol, you can communicate to each path using one (or more) of these "methods".

When building APIs, you normally use these specific HTTP methods to perform a specific action.

Normally you use:

POST : to create data.

GET : to read data.

PUT : to update data.

DELETE : to delete data.

So, in OpenAPI, each of the HTTP methods is called an "operation".

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
We are going to call them "operations" too.

Define a path operation decorator

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
return {"message": "Hello World"}

The @app.get("/") tells FastAPI that the function right below is in charge of handling requests that go to:

the path /

using a get operation

@decorator Info

That @something syntax in Python is called a "decorator".

You put it on top of a function. Like a pretty decorative hat (I guess that's where the term came from).

A "decorator" takes the function below and does something with it.

In our case, this decorator tells FastAPI that the function below corresponds to the path / with an operation get .

It is the "path operation decorator".

You can also use the other operations:

@app.post()

@app.put()

@app.delete()

And the more exotic ones:

@app.options()

@app.head()

@app.patch()

@app.trace()

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Tip

You are free to use each operation (HTTP method) as you wish.

FastAPI doesn't enforce any specific meaning.

The information here is presented as a guideline, not a requirement.

For example, when using GraphQL you normally perform all the actions using only POST operations.

Step 4: define the path operation function

This is our "path operation function":

path: is / .

operation: is get .

function: is the function below the "decorator" (below @app.get("/") ).

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
return {"message": "Hello World"}

This is a Python function.

It will be called by FastAPI whenever it receives a request to the URL " / " using a GET operation.

In this case, it is an async function.

You could also define it as a normal function instead of async def :

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def root():
return {"message": "Hello World"}

Note

If you don't know the difference, check the Async: "In a hurry?" ↪.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Step 5: return the content

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def root():
return {"message": "Hello World"}

You can return a dict , list , singular values as str , int , etc.

You can also return Pydantic models (you'll see more about that later).

There are many other objects and models that will be automatically converted to JSON (including ORMs,
etc). Try using your favorite ones, it's highly probable that they are already supported.

Recap
Import FastAPI .

Create an app instance.

Write a path operation decorator using decorators like @app.get("/") .

Define a path operation function; for example, def root(): ... .

Run the development server using the command fastapi dev .

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Path Parameters

You can declare path "parameters" or "variables" with the same syntax used by Python format
strings:

from fastapi import FastAPI

app = FastAPI()

@app.get("/items/{item_id}")
async def read_item(item_id):
return {"item_id": item_id}

The value of the path parameter item_id will be passed to your function as the argument item_id .

So, if you run this example and go to http://127.0.0.1:8000/items/foo [↪], you will see a response of:

{"item_id":"foo"}

Path parameters with types


You can declare the type of a path parameter in the function, using standard Python type
annotations:

from fastapi import FastAPI

app = FastAPI()

@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id}

In this case, item_id is declared to be an int .

Check

This will give you editor support inside of your function, with error checks, completion, etc.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Data conversion
If you run this example and open your browser at http://127.0.0.1:8000/items/3 [↪], you will see a
response of:

{"item_id":3}

Check

Notice that the value your function received (and returned) is 3 , as a Python int , not a string "3" .

So, with that type declaration, FastAPI gives you automatic request "parsing".

Data validation
But if you go to the browser at http://127.0.0.1:8000/items/foo [↪], you will see a nice HTTP error of:

{
"detail": [
{
"type": "int_parsing",
"loc": [
"path",
"item_id"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "foo",
"url": "https://errors.pydantic.dev/2.1/v/int_parsing"
}
]
}

because the path parameter item_id had a value of "foo" , which is not an int .

The same error would appear if you provided a float instead of an int , as in:
http://127.0.0.1:8000/items/4.2 [↪]

Check

So, with the same Python type declaration, FastAPI gives you data validation.

Notice that the error also clearly states exactly the point where the validation didn't pass.

This is incredibly helpful while developing and debugging code that interacts with your API.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Documentation
And when you open your browser at http://127.0.0.1:8000/docs [↪], you will see an automatic,
interactive, API documentation like:

Check

Again, just with that same Python type declaration, FastAPI gives you automatic, interactive documentation (integrating
Swagger UI).

Notice that the path parameter is declared to be an integer.

Standards-based benefits, alternative documentation

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
And because the generated schema is from the OpenAPI [↪] standard, there are many compatible
tools.

Because of this, FastAPI itself provides an alternative API documentation (using ReDoc), which you
can access at http://127.0.0.1:8000/redoc [↪]:

The same way, there are many compatible tools. Including code generation tools for many
languages.

Pydantic
All the data validation is performed under the hood by Pydantic [↪], so you get all the benefits from
it. And you know you are in good hands.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
You can use the same type declarations with str , float , bool and many other complex data
types.

Several of these are explored in the next chapters of the tutorial.

Order matters
When creating path operations, you can find situations where you have a fixed path.

Like /users/me , let's say that it's to get data about the current user.

And then you can also have a path /users/{user_id} to get data about a specific user by some
user ID.

Because path operations are evaluated in order, you need to make sure that the path for /users/me
is declared before the one for /users/{user_id} :

from fastapi import FastAPI

app = FastAPI()

@app.get("/users/me")
async def read_user_me():
return {"user_id": "the current user"}

@app.get("/users/{user_id}")
async def read_user(user_id: str):
return {"user_id": user_id}

Otherwise, the path for /users/{user_id} would match also for /users/me , "thinking" that it's
receiving a parameter user_id with a value of "me" .

Similarly, you cannot redefine a path operation:

from fastapi import FastAPI

app = FastAPI()

@app.get("/users")
async def read_users():
return ["Rick", "Morty"]

@app.get("/users")

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
async def read_users2():
return ["Bean", "Elfo"]

The first one will always be used since the path matches first.

Predefined values
If you have a path operation that receives a path parameter, but you want the possible valid path
parameter values to be predefined, you can use a standard Python Enum .

Create an Enum class

Import Enum and create a sub-class that inherits from str and from Enum .

By inheriting from str the API docs will be able to know that the values must be of type string
and will be able to render correctly.

Then create class attributes with fixed values, which will be the available valid values:

from enum import Enum

from fastapi import FastAPI

class ModelName(str, Enum):


alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"

app = FastAPI()

@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
if model_name is ModelName.alexnet:
return {"model_name": model_name, "message": "Deep Learning FTW!"}

if model_name.value == "lenet":
return {"model_name": model_name, "message": "LeCNN all the images"}

return {"model_name": model_name, "message": "Have some residuals"}

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Info

Enumerations (or enums) are available in Python [↪] since version 3.4.

Tip

If you are wondering, "AlexNet", "ResNet", and "LeNet" are just names of Machine Learning models.

Declare a path parameter

Then create a path parameter with a type annotation using the enum class you created ( ModelName
):

from enum import Enum

from fastapi import FastAPI

class ModelName(str, Enum):


alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"

app = FastAPI()

@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
if model_name is ModelName.alexnet:
return {"model_name": model_name, "message": "Deep Learning FTW!"}

if model_name.value == "lenet":
return {"model_name": model_name, "message": "LeCNN all the images"}

return {"model_name": model_name, "message": "Have some residuals"}

Check the docs

Because the available values for the path parameter are predefined, the interactive docs can show
them nicely:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Working with Python enumerations

The value of the path parameter will be an enumeration member.

Compare enumeration members

You can compare it with the enumeration member in your created enum ModelName :

from enum import Enum

from fastapi import FastAPI

class ModelName(str, Enum):


alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"

app = FastAPI()

@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
if model_name is ModelName.alexnet:
return {"model_name": model_name, "message": "Deep Learning FTW!"}

if model_name.value == "lenet":
return {"model_name": model_name, "message": "LeCNN all the images"}

return {"model_name": model_name, "message": "Have some residuals"}

Get the enumeration value

You can get the actual value (a str in this case) using model_name.value , or in general,
your_enum_member.value :

from enum import Enum

from fastapi import FastAPI

class ModelName(str, Enum):


alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
app = FastAPI()

@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
if model_name is ModelName.alexnet:
return {"model_name": model_name, "message": "Deep Learning FTW!"}

if model_name.value == "lenet":
return {"model_name": model_name, "message": "LeCNN all the images"}

return {"model_name": model_name, "message": "Have some residuals"}

Tip

You could also access the value "lenet" with ModelName.lenet.value .

Return enumeration members

You can return enum members from your path operation, even nested in a JSON body (e.g. a dict ).

They will be converted to their corresponding values (strings in this case) before returning them to
the client:

from enum import Enum

from fastapi import FastAPI

class ModelName(str, Enum):


alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"

app = FastAPI()

@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
if model_name is ModelName.alexnet:
return {"model_name": model_name, "message": "Deep Learning FTW!"}

if model_name.value == "lenet":
return {"model_name": model_name, "message": "LeCNN all the images"}

return {"model_name": model_name, "message": "Have some residuals"}

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
In your client you will get a JSON response like:

{
"model_name": "alexnet",
"message": "Deep Learning FTW!"
}

Path parameters containing paths


Let's say you have a path operation with a path /files/{file_path} .

But you need file_path itself to contain a path, like home/johndoe/myfile.txt .

So, the URL for that file would be something like: /files/home/johndoe/myfile.txt .

OpenAPI support

OpenAPI doesn't support a way to declare a path parameter to contain a path inside, as that could
lead to scenarios that are difficult to test and define.

Nevertheless, you can still do it in FastAPI, using one of the internal tools from Starlette.

And the docs would still work, although not adding any documentation telling that the parameter
should contain a path.

Path convertor

Using an option directly from Starlette you can declare a path parameter containing a path using a
URL like:

/files/{file_path:path}

In this case, the name of the parameter is file_path , and the last part, :path , tells it that the
parameter should match any path.

So, you can use it with:

from fastapi import FastAPI

app = FastAPI()

@app.get("/files/{file_path:path}")

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
async def read_file(file_path: str):
return {"file_path": file_path}

Tip

You could need the parameter to contain /home/johndoe/myfile.txt , with a leading slash ( / ).

In that case, the URL would be: /files//home/johndoe/myfile.txt , with a double slash ( // ) between files and
home .

Recap
With FastAPI, by using short, intuitive and standard Python type declarations, you get:

Editor support: error checks, autocompletion, etc.

Data "parsing"

Data validation

API annotation and automatic documentation

And you only have to declare them once.

That's probably the main visible advantage of FastAPI compared to alternative frameworks (apart
from the raw performance).

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Query Parameters

When you declare other function parameters that are not part of the path parameters, they are
automatically interpreted as "query" parameters.

from fastapi import FastAPI

app = FastAPI()

fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]

@app.get("/items/")
async def read_item(skip: int = 0, limit: int = 10):
return fake_items_db[skip : skip + limit]

The query is the set of key-value pairs that go after the ? in a URL, separated by & characters.

For example, in the URL:

http://127.0.0.1:8000/items/?skip=0&limit=10

...the query parameters are:

skip : with a value of 0

limit : with a value of 10

As they are part of the URL, they are "naturally" strings.

But when you declare them with Python types (in the example above, as int ), they are converted to
that type and validated against it.

All the same process that applied for path parameters also applies for query parameters:

Editor support (obviously)

Data "parsing"

Data validation

Automatic documentation

Defaults

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
As query parameters are not a fixed part of a path, they can be optional and can have default values.

In the example above they have default values of skip=0 and limit=10 .

So, going to the URL:

http://127.0.0.1:8000/items/

would be the same as going to:

http://127.0.0.1:8000/items/?skip=0&limit=10

But if you go to, for example:

http://127.0.0.1:8000/items/?skip=20

The parameter values in your function will be:

skip=20 : because you set it in the URL

limit=10 : because that was the default value

Optional parameters
The same way, you can declare optional query parameters, by setting their default to None :

Python 3.10+ Python 3.8+

from fastapi import FastAPI

app = FastAPI()

@app.get("/items/{item_id}")
async def read_item(item_id: str, q: str | None = None):
if q:
return {"item_id": item_id, "q": q}
return {"item_id": item_id}

In this case, the function parameter q will be optional, and will be None by default.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Check

Also notice that FastAPI is smart enough to notice that the path parameter item_id is a path parameter and q is not,
so, it's a query parameter.

Query parameter type conversion


You can also declare bool types, and they will be converted:

Python 3.10+ Python 3.8+

from fastapi import FastAPI

app = FastAPI()

@app.get("/items/{item_id}")
async def read_item(item_id: str, q: str | None = None, short: bool = False):
item = {"item_id": item_id}
if q:
item.update({"q": q})
if not short:
item.update(
{"description": "This is an amazing item that has a long description"}
)
return item

In this case, if you go to:

http://127.0.0.1:8000/items/foo?short=1

or

http://127.0.0.1:8000/items/foo?short=True

or

http://127.0.0.1:8000/items/foo?short=true

or

http://127.0.0.1:8000/items/foo?short=on

or

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
http://127.0.0.1:8000/items/foo?short=yes

or any other case variation (uppercase, first letter in uppercase, etc), your function will see the
parameter short with a bool value of True . Otherwise as False .

Multiple path and query parameters


You can declare multiple path parameters and query parameters at the same time, FastAPI knows
which is which.

And you don't have to declare them in any specific order.

They will be detected by name:

Python 3.10+ Python 3.8+

from fastapi import FastAPI

app = FastAPI()

@app.get("/users/{user_id}/items/{item_id}")
async def read_user_item(
user_id: int, item_id: str, q: str | None = None, short: bool = False
):
item = {"item_id": item_id, "owner_id": user_id}
if q:
item.update({"q": q})
if not short:
item.update(
{"description": "This is an amazing item that has a long description"}
)
return item

Required query parameters


When you declare a default value for non-path parameters (for now, we have only seen query
parameters), then it is not required.

If you don't want to add a specific value but just make it optional, set the default as None .

But when you want to make a query parameter required, you can just not declare any default value:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
from fastapi import FastAPI

app = FastAPI()

@app.get("/items/{item_id}")
async def read_user_item(item_id: str, needy: str):
item = {"item_id": item_id, "needy": needy}
return item

Here the query parameter needy is a required query parameter of type str .

If you open in your browser a URL like:

http://127.0.0.1:8000/items/foo-item

...without adding the required parameter needy , you will see an error like:

{
"detail": [
{
"type": "missing",
"loc": [
"query",
"needy"
],
"msg": "Field required",
"input": null,
"url": "https://errors.pydantic.dev/2.1/v/missing"
}
]
}

As needy is a required parameter, you would need to set it in the URL:

http://127.0.0.1:8000/items/foo-item?needy=sooooneedy

...this would work:

{
"item_id": "foo-item",
"needy": "sooooneedy"
}

And of course, you can define some parameters as required, some as having a default value, and
some entirely optional:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Python 3.10+ Python 3.8+

from fastapi import FastAPI

app = FastAPI()

@app.get("/items/{item_id}")
async def read_user_item(
item_id: str, needy: str, skip: int = 0, limit: int | None = None
):
item = {"item_id": item_id, "needy": needy, "skip": skip, "limit": limit}
return item

In this case, there are 3 query parameters:

needy , a required str .

skip , an int with a default value of 0 .

limit , an optional int .

Tip

You could also use Enum s the same way as with Path Parameters ↪.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Request Body

When you need to send data from a client (let's say, a browser) to your API, you send it as a request
body.

A request body is data sent by the client to your API. A response body is the data your API sends to
the client.

Your API almost always has to send a response body. But clients don't necessarily need to send
request bodies all the time.

To declare a request body, you use Pydantic [↪] models with all their power and benefits.

Info

To send data, you should use one of: POST (the more common), PUT , DELETE or PATCH .

Sending a body with a GET request has an undefined behavior in the specifications, nevertheless, it is supported by
FastAPI, only for very complex/extreme use cases.

As it is discouraged, the interactive docs with Swagger UI won't show the documentation for the body when using GET ,
and proxies in the middle might not support it.

Import Pydantic's BaseModel


First, you need to import BaseModel from pydantic :

Python 3.10+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None

app = FastAPI()

@app.post("/items/")

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
async def create_item(item: Item):
return item

Create your data model


Then you declare your data model as a class that inherits from BaseModel .

Use standard Python types for all the attributes:

Python 3.10+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None

app = FastAPI()

@app.post("/items/")
async def create_item(item: Item):
return item

The same as when declaring query parameters, when a model attribute has a default value, it is not
required. Otherwise, it is required. Use None to make it just optional.

For example, this model above declares a JSON " object " (or Python dict ) like:

{
"name": "Foo",
"description": "An optional description",
"price": 45.2,
"tax": 3.5
}

...as description and tax are optional (with a default value of None ), this JSON " object " would
also be valid:

{
"name": "Foo",

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
"price": 45.2
}

Declare it as a parameter
To add it to your path operation, declare it the same way you declared path and query parameters:

Python 3.10+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None

app = FastAPI()

@app.post("/items/")
async def create_item(item: Item):
return item

...and declare its type as the model you created, Item .

Results
With just that Python type declaration, FastAPI will:

Read the body of the request as JSON.

Convert the corresponding types (if needed).

Validate the data.

If the data is invalid, it will return a nice and clear error, indicating exactly where and what
was the incorrect data.

Give you the received data in the parameter item .

As you declared it in the function to be of type Item , you will also have all the editor
support (completion, etc) for all of the attributes and their types.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Generate JSON Schema [↪] definitions for your model, you can also use them anywhere else
you like if it makes sense for your project.

Those schemas will be part of the generated OpenAPI schema, and used by the automatic
documentation UIs.

Automatic docs
The JSON Schemas of your models will be part of your OpenAPI generated schema, and will be
shown in the interactive API docs:

And will be also used in the API docs inside each path operation that needs them:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Editor support
In your editor, inside your function you will get type hints and completion everywhere (this wouldn't
happen if you received a dict instead of a Pydantic model):

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
You also get error checks for incorrect type operations:

This is not by chance, the whole framework was built around that design.

And it was thoroughly tested at the design phase, before any implementation, to ensure it would
work with all the editors.

There were even some changes to Pydantic itself to support this.

The previous screenshots were taken with Visual Studio Code [↪].

But you would get the same editor support with PyCharm [↪] and most of the other Python editors:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Tip

If you use PyCharm [↪] as your editor, you can use the Pydantic PyCharm Plugin [↪].

It improves editor support for Pydantic models, with:

auto-completion

type checks

refactoring

searching

inspections

Use the model


Inside of the function, you can access all the attributes of the model object directly:

Python 3.10+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel

class Item(BaseModel):
name: str

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
description: str | None = None
price: float
tax: float | None = None

app = FastAPI()

@app.post("/items/")
async def create_item(item: Item):
item_dict = item.dict()
if item.tax:
price_with_tax = item.price + item.tax
item_dict.update({"price_with_tax": price_with_tax})
return item_dict

Request body + path parameters


You can declare path parameters and request body at the same time.

FastAPI will recognize that the function parameters that match path parameters should be taken
from the path, and that function parameters that are declared to be Pydantic models should be
taken from the request body.

Python 3.10+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None

app = FastAPI()

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
return {"item_id": item_id, **item.dict()}

Request body + path + query parameters

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
You can also declare body, path and query parameters, all at the same time.

FastAPI will recognize each of them and take the data from the correct place.

Python 3.10+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None

app = FastAPI()

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, q: str | None = None):
result = {"item_id": item_id, **item.dict()}
if q:
result.update({"q": q})
return result

The function parameters will be recognized as follows:

If the parameter is also declared in the path, it will be used as a path parameter.

If the parameter is of a singular type (like int , float , str , bool , etc) it will be interpreted as
a query parameter.

If the parameter is declared to be of the type of a Pydantic model, it will be interpreted as a


request body.

Note

FastAPI will know that the value of q is not required because of the default value = None .

The Union in Union[str, None] is not used by FastAPI, but will allow your editor to give you better support and detect
errors.

Without Pydantic

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
If you don't want to use Pydantic models, you can also use Body parameters. See the docs for Body -
Multiple Parameters: Singular values in body ↪.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Query Parameters and String Validations

FastAPI allows you to declare additional information and validation for your parameters.

Let's take this application as example:

Python 3.10+ Python 3.8+

from fastapi import FastAPI

app = FastAPI()

@app.get("/items/")
async def read_items(q: str | None = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results

The query parameter q is of type Union[str, None] (or str | None in Python 3.10), that means that it's of type str but
could also be None , and indeed, the default value is None , so FastAPI will know it's not required.

Note

FastAPI will know that the value of q is not required because of the default value = None .

The Union in Union[str, None] will allow your editor to give you better support and detect errors.

Additional validation
We are going to enforce that even though q is optional, whenever it is provided, its length doesn't exceed 50 characters.

Import Query and Annotated

To achieve that, first import:

Query from fastapi

Annotated from typing (or from typing_extensions in Python below 3.9)

Python 3.10+ Python 3.8+

In Python 3.9 or above, Annotated is part of the standard library, so you can import it from typing .

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(max_length=50)] = None):

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results

Info

FastAPI added support for Annotated (and started recommending it) in version 0.95.0.

If you have an older version, you would get errors when trying to use Annotated .

Make sure you Upgrade the FastAPI version ↪ to at least 0.95.1 before using Annotated .

Use Annotated in the type for the q parameter


Remember I told you before that Annotated can be used to add metadata to your parameters in the Python Types Intro ↪?

Now it's the time to use it with FastAPI. 🚀


We had this type annotation:

Python 3.10+ Python 3.8+

q: str | None = None

What we will do is wrap that with Annotated , so it becomes:

Python 3.10+ Python 3.8+

q: Annotated[str | None] = None

Both of those versions mean the same thing, q is a parameter that can be a str or None , and by default, it is None .

Now let's jump to the fun stuff. 🎉


Add Query to Annotated in the q parameter
Now that we have this Annotated where we can put more information (in this case some additional validation), add Query
inside of Annotated , and set the parameter max_length to 50 :

Python 3.10+ Python 3.8+

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(max_length=50)] = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
results.update({"q": q})
return results

Notice that the default value is still None , so the parameter is still optional.

But now, having Query(max_length=50) inside of Annotated , we are telling FastAPI that we want it to have additional
validation for this value, we want it to have maximum 50 characters. 😎
Tip

Here we are using Query() because this is a query parameter. Later we will see others like Path() , Body() , Header() , and Cookie() , that also
accept the same arguments as Query() .

FastAPI will now:

Validate the data making sure that the max length is 50 characters

Show a clear error for the client when the data is not valid

Document the parameter in the OpenAPI schema path operation (so it will show up in the automatic docs UI)

Alternative (old) Query as the default value


Previous versions of FastAPI (before 0.95.0) required you to use Query as the default value of your parameter, instead of
putting it in Annotated , there's a high chance that you will see code using it around, so I'll explain it to you.

Tip

For new code and whenever possible, use Annotated as explained above. There are multiple advantages (explained below) and no disadvantages.
🍰
This is how you would use Query() as the default value of your function parameter, setting the parameter max_length to
50:

Python 3.10+ Python 3.8+

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: str | None = Query(default=None, max_length=50)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results

As in this case (without using Annotated ) we have to replace the default value None in the function with Query() , we
now need to set the default value with the parameter Query(default=None) , it serves the same purpose of defining that
default value (at least for FastAPI).

So:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
q: Union[str, None] = Query(default=None)

...makes the parameter optional, with a default value of None , the same as:

q: Union[str, None] = None

And in Python 3.10 and above:

q: str | None = Query(default=None)

...makes the parameter optional, with a default value of None , the same as:

q: str | None = None

But it declares it explicitly as being a query parameter.

Info

Keep in mind that the most important part to make a parameter optional is the part:

= None

or the:

= Query(default=None)

as it will use that None as the default value, and that way make the parameter not required.

The Union[str, None] part allows your editor to provide better support, but it is not what tells FastAPI that this parameter is not required.

Then, we can pass more parameters to Query . In this case, the max_length parameter that applies to strings:

q: Union[str, None] = Query(default=None, max_length=50)

This will validate the data, show a clear error when the data is not valid, and document the parameter in the OpenAPI
schema path operation.

Query as the default value or in Annotated

Keep in mind that when using Query inside of Annotated you cannot use the default parameter for Query .

Instead use the actual default value of the function parameter. Otherwise, it would be inconsistent.

For example, this is not allowed:

q: Annotated[str, Query(default="rick")] = "morty"

...because it's not clear if the default value should be "rick" or "morty" .

So, you would use (preferably):

q: Annotated[str, Query()] = "rick"

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
...or in older code bases you will find:

q: str = Query(default="rick")

Advantages of Annotated

Using Annotated is recommended instead of the default value in function parameters, it is better for multiple reasons. 🤓
The default value of the function parameter is the actual default value, that's more intuitive with Python in general. 😌
You could call that same function in other places without FastAPI, and it would work as expected. If there's a required
parameter (without a default value), your editor will let you know with an error, Python will also complain if you run it
without passing the required parameter.

When you don't use Annotated and instead use the (old) default value style, if you call that function without FastAPI in
other place, you have to remember to pass the arguments to the function for it to work correctly, otherwise the values will
be different from what you expect (e.g. QueryInfo or something similar instead of str ). And your editor won't complain,
and Python won't complain running that function, only when the operations inside error out.

Because Annotated can have more than one metadata annotation, you could now even use the same function with other
tools, like Typer [↪]. 🚀
Add more validations
You can also add a parameter min_length :

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(
q: Annotated[str | None, Query(min_length=3, max_length=50)] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results

Add regular expressions


You can define a regular expression pattern that the parameter should match:

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, Query

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
app = FastAPI()

@app.get("/items/")
async def read_items(
q: Annotated[
str | None, Query(min_length=3, max_length=50, pattern="^fixedquery$")
] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results

This specific regular expression pattern checks that the received parameter value:

^ : starts with the following characters, doesn't have characters before.

fixedquery : has the exact value fixedquery .

$ : ends there, doesn't have any more characters after fixedquery .

If you feel lost with all these "regular expression" ideas, don't worry. They are a hard topic for many people. You can still do
a lot of stuff without needing regular expressions yet.

But whenever you need them and go and learn them, know that you can already use them directly in FastAPI.

Pydantic v1 regex instead of pattern

Before Pydantic version 2 and before FastAPI 0.100.0, the parameter was called regex instead of pattern , but it's now
deprecated.

You could still see some code using it:

Python 3.10+ Pydantic v1

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(
q: Annotated[
str | None, Query(min_length=3, max_length=50, regex="^fixedquery$")
] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results

But know that this is deprecated and it should be updated to use the new parameter pattern . 🤓
Default values
You can, of course, use default values other than None .

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Let's say that you want to declare the q query parameter to have a min_length of 3 , and to have a default value of
"fixedquery" :

Python 3.9+ Python 3.8+ Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)] = "fixedquery"):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results

Note

Having a default value of any type, including None , makes the parameter optional (not required).

Make it required
When we don't need to declare more validations or metadata, we can make the q query parameter required just by not
declaring a default value, like:

q: str

instead of:

q: Union[str, None] = None

But we are now declaring it with Query , for example like:

Annotated non-Annotated

q: Annotated[Union[str, None], Query(min_length=3)] = None

So, when you need to declare a value as required while using Query , you can simply not declare a default value:

Python 3.9+ Python 3.8+ Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)]):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
results.update({"q": q})
return results

Required with Ellipsis ( ... )

There's an alternative way to explicitly declare that a value is required. You can set the default to the literal value ... :

Python 3.9+ Python 3.8+ Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[str, Query(min_length=3)] = ...):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results

Info

If you hadn't seen that ... before: it is a special single value, it is part of Python and is called "Ellipsis" [↪].

It is used by Pydantic and FastAPI to explicitly declare that a value is required.

This will let FastAPI know that this parameter is required.

Required with None

You can declare that a parameter can accept None , but that it's still required. This would force clients to send a value, even
if the value is None .

To do that, you can declare that None is a valid type but still use ... as the default:

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(min_length=3)] = ...):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Tip

Pydantic, which is what powers all the data validation and serialization in FastAPI, has a special behavior when you use Optional or
Union[Something, None] without a default value, you can read more about it in the Pydantic docs about Required Optional fields [↪].

Tip

Remember that in most of the cases, when something is required, you can simply omit the default, so you normally don't have to use ... .

Query parameter list / multiple values


When you define a query parameter explicitly with Query you can also declare it to receive a list of values, or said in other
way, to receive multiple values.

For example, to declare a query parameter q that can appear multiple times in the URL, you can write:

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.9+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[list[str] | None, Query()] = None):
query_items = {"q": q}
return query_items

Then, with a URL like:

http://localhost:8000/items/?q=foo&q=bar

you would receive the multiple q query parameters' values ( foo and bar ) in a Python list inside your path operation
function, in the function parameter q .

So, the response to that URL would be:

{
"q": [
"foo",
"bar"
]
}

Tip

To declare a query parameter with a type of list , like in the example above, you need to explicitly use Query , otherwise it would be interpreted as a
request body.

The interactive API docs will update accordingly, to allow multiple values:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Query parameter list / multiple values with defaults

And you can also define a default list of values if none are provided:

Python 3.9+ Python 3.8+ Python 3.9+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, Query

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[list[str], Query()] = ["foo", "bar"]):
query_items = {"q": q}
return query_items

If you go to:

http://localhost:8000/items/

the default of q will be: ["foo", "bar"] and your response will be:

{
"q": [
"foo",
"bar"
]
}

Using list

You can also use list directly instead of List[str] (or list[str] in Python 3.9+):

Python 3.9+ Python 3.8+ Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[list, Query()] = []):
query_items = {"q": q}
return query_items

Note

Keep in mind that in this case, FastAPI won't check the contents of the list.

For example, List[int] would check (and document) that the contents of the list are integers. But list alone wouldn't.

Declare more metadata


You can add more information about the parameter.

That information will be included in the generated OpenAPI and used by the documentation user interfaces and external
tools.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Note

Keep in mind that different tools might have different levels of OpenAPI support.

Some of them might not show all the extra information declared yet, although in most of the cases, the missing feature is already planned for
development.

You can add a title :

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(
q: Annotated[str | None, Query(title="Query string", min_length=3)] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results

And a description :

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(
q: Annotated[
str | None,
Query(
title="Query string",
description="Query string for the items to search in the database that have a good match",
min_length=3,
),
] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results

Alias parameters
Imagine that you want the parameter to be item-query .

Like in:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
http://127.0.0.1:8000/items/?item-query=foobaritems

But item-query is not a valid Python variable name.

The closest would be item_query .

But you still need it to be exactly item-query ...

Then you can declare an alias , and that alias is what will be used to find the parameter value:

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(alias="item-query")] = None):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results

Deprecating parameters
Now let's say you don't like this parameter anymore.

You have to leave it there a while because there are clients using it, but you want the docs to clearly show it as deprecated.

Then pass the parameter deprecated=True to Query :

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

@app.get("/items/")
async def read_items(
q: Annotated[
str | None,
Query(
alias="item-query",
title="Query string",
description="Query string for the items to search in the database that have a good match",
min_length=3,
max_length=50,
pattern="^fixedquery$",
deprecated=True,
),
] = None,
):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
if q:
results.update({"q": q})
return results

The docs will show it like this:

Exclude from OpenAPI


To exclude a query parameter from the generated OpenAPI schema (and thus, from the automatic documentation
systems), set the parameter include_in_schema of Query to False :

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, Query

app = FastAPI()

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
@app.get("/items/")
async def read_items(
hidden_query: Annotated[str | None, Query(include_in_schema=False)] = None,
):
if hidden_query:
return {"hidden_query": hidden_query}
else:
return {"hidden_query": "Not found"}

Recap
You can declare additional validations and metadata for your parameters.

Generic validations and metadata:

alias

title

description

deprecated

Validations specific for strings:

min_length

max_length

pattern

In these examples you saw how to declare validations for str values.

See the next chapters to see how to declare validations for other types, like numbers.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Path Parameters and Numeric Validations

In the same way that you can declare more validations and metadata for query parameters with Query ,
you can declare the same type of validations and metadata for path parameters with Path .

Import Path
First, import Path from fastapi , and import Annotated :

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, Path, Query

app = FastAPI()

@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get")],
q: Annotated[str | None, Query(alias="item-query")] = None,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results

Info

FastAPI added support for Annotated (and started recommending it) in version 0.95.0.

If you have an older version, you would get errors when trying to use Annotated .

Make sure you Upgrade the FastAPI version ↪ to at least 0.95.1 before using Annotated .

Declare metadata
You can declare all the same parameters as for Query .

For example, to declare a title metadata value for the path parameter item_id you can type:

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
from typing import Annotated

from fastapi import FastAPI, Path, Query

app = FastAPI()

@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get")],
q: Annotated[str | None, Query(alias="item-query")] = None,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results

Note

A path parameter is always required as it has to be part of the path. Even if you declared it with None or set a default value, it
would not affect anything, it would still be always required.

Order the parameters as you need

Tip

This is probably not as important or necessary if you use Annotated .

Let's say that you want to declare the query parameter q as a required str .

And you don't need to declare anything else for that parameter, so you don't really need to use Query .

But you still need to use Path for the item_id path parameter. And you don't want to use Annotated
for some reason.

Python will complain if you put a value with a "default" before a value that doesn't have a "default".

But you can re-order them, and have the value without a default (the query parameter q ) first.

It doesn't matter for FastAPI. It will detect the parameters by their names, types and default
declarations ( Query , Path , etc), it doesn't care about the order.

So, you can declare your function as:

Python 3.8 non-Annotated

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Tip

Prefer to use the Annotated version if possible.

from fastapi import FastAPI, Path

app = FastAPI()

@app.get("/items/{item_id}")
async def read_items(q: str, item_id: int = Path(title="The ID of the item to get")):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results

But keep in mind that if you use Annotated , you won't have this problem, it won't matter as you're not
using the function parameter default values for Query() or Path() .

Python 3.9+ Python 3.8+

from typing import Annotated

from fastapi import FastAPI, Path

app = FastAPI()

@app.get("/items/{item_id}")
async def read_items(
q: str, item_id: Annotated[int, Path(title="The ID of the item to get")]
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results

Order the parameters as you need, tricks

Tip

This is probably not as important or necessary if you use Annotated .

Here's a small trick that can be handy, but you won't need it often.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
If you want to:

declare the q query parameter without a Query nor any default value

declare the path parameter item_id using Path

have them in a different order

not use Annotated

...Python has a little special syntax for that.

Pass * , as the first parameter of the function.

Python won't do anything with that * , but it will know that all the following parameters should be called
as keyword arguments (key-value pairs), also known as kwargs . Even if they don't have a default value.

from fastapi import FastAPI, Path

app = FastAPI()

@app.get("/items/{item_id}")
async def read_items(*, item_id: int = Path(title="The ID of the item to get"), q: str):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results

Better with Annotated

Keep in mind that if you use Annotated , as you are not using function parameter default values, you
won't have this problem, and you probably won't need to use * .

Python 3.9+ Python 3.8+

from typing import Annotated

from fastapi import FastAPI, Path

app = FastAPI()

@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get")], q: str
):
results = {"item_id": item_id}
if q:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
results.update({"q": q})
return results

Number validations: greater than or equal


With Query and Path (and others you'll see later) you can declare number constraints.

Here, with ge=1 , item_id will need to be an integer number " g reater than or e qual" to 1 .

Python 3.9+ Python 3.8+ Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, Path

app = FastAPI()

@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get", ge=1)], q: str
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results

Number validations: greater than and less than or equal


The same applies for:

gt : g reater t han

le : l ess than or e qual

Python 3.9+ Python 3.8+ Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, Path

app = FastAPI()

@app.get("/items/{item_id}")
async def read_items(
item_id: Annotated[int, Path(title="The ID of the item to get", gt=0, le=1000)],

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
q: str,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results

Number validations: floats, greater than and less than


Number validations also work for float values.

Here's where it becomes important to be able to declare gt and not just ge . As with it you can require,
for example, that a value must be greater than 0 , even if it is less than 1 .

So, 0.5 would be a valid value. But 0.0 or 0 would not.

And the same for lt .

Python 3.9+ Python 3.8+ Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, Path, Query

app = FastAPI()

@app.get("/items/{item_id}")
async def read_items(
*,
item_id: Annotated[int, Path(title="The ID of the item to get", ge=0, le=1000)],
q: str,
size: Annotated[float, Query(gt=0, lt=10.5)],
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
return results

Recap
With Query , Path (and others you haven't seen yet) you can declare metadata and string validations in
the same ways as with Query Parameters and String Validations ↪.

And you can also declare numeric validations:

gt : g reater t han

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
ge : g reater than or e qual

lt : l ess t han

le : l ess than or e qual

Info

Query , Path , and other classes you will see later are subclasses of a common Param class.

All of them share the same parameters for additional validation and metadata you have seen.

Technical Details

When you import Query , Path and others from fastapi , they are actually functions.

That when called, return instances of classes of the same name.

So, you import Query , which is a function. And when you call it, it returns an instance of a class also named Query .

These functions are there (instead of just using the classes directly) so that your editor doesn't mark errors about their types.

That way you can use your normal editor and coding tools without having to add custom configurations to disregard those
errors.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Body - Multiple Parameters

Now that we have seen how to use Path and Query , let's see more advanced uses of request body
declarations.

Mix Path , Query and body parameters


First, of course, you can mix Path , Query and request body parameter declarations freely and FastAPI will
know what to do.

And you can also declare body parameters as optional, by setting the default to None :

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, Path


from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None

@app.put("/items/{item_id}")
async def update_item(
item_id: Annotated[int, Path(title="The ID of the item to get", ge=0, le=1000)],
q: str | None = None,
item: Item | None = None,
):
results = {"item_id": item_id}
if q:
results.update({"q": q})
if item:
results.update({"item": item})
return results

Note

Notice that, in this case, the item that would be taken from the body is optional. As it has a None default value.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Multiple body parameters
In the previous example, the path operations would expect a JSON body with the attributes of an Item , like:

{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}

But you can also declare multiple body parameters, e.g. item and user :

Python 3.10+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None

class User(BaseModel):
username: str
full_name: str | None = None

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, user: User):
results = {"item_id": item_id, "item": item, "user": user}
return results

In this case, FastAPI will notice that there are more than one body parameters in the function (two
parameters that are Pydantic models).

So, it will then use the parameter names as keys (field names) in the body, and expect a body like:

{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
},
"user": {
"username": "dave",

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
"full_name": "Dave Grohl"
}
}

Note

Notice that even though the item was declared the same way as before, it is now expected to be inside of the body with a key
item .

FastAPI will do the automatic conversion from the request, so that the parameter item receives its
specific content and the same for user .

It will perform the validation of the compound data, and will document it like that for the OpenAPI schema
and automatic docs.

Singular values in body


The same way there is a Query and Path to define extra data for query and path parameters, FastAPI
provides an equivalent Body .

For example, extending the previous model, you could decide that you want to have another key
importance in the same body, besides the item and user .

If you declare it as is, because it is a singular value, FastAPI will assume that it is a query parameter.

But you can instruct FastAPI to treat it as another body key using Body :

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Body, FastAPI


from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None

class User(BaseModel):
username: str
full_name: str | None = None

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
@app.put("/items/{item_id}")
async def update_item(
item_id: int, item: Item, user: User, importance: Annotated[int, Body()]
):
results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
return results

In this case, FastAPI will expect a body like:

{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
},
"user": {
"username": "dave",
"full_name": "Dave Grohl"
},
"importance": 5
}

Again, it will convert the data types, validate, document, etc.

Multiple body params and query


Of course, you can also declare additional query parameters whenever you need, additional to any body
parameters.

As, by default, singular values are interpreted as query parameters, you don't have to explicitly add a Query
, you can just do:

q: Union[str, None] = None

Or in Python 3.10 and above:

q: str | None = None

For example:

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Body, FastAPI


from pydantic import BaseModel

app = FastAPI()

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None

class User(BaseModel):
username: str
full_name: str | None = None

@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Item,
user: User,
importance: Annotated[int, Body(gt=0)],
q: str | None = None,
):
results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
if q:
results.update({"q": q})
return results

Info

Body also has all the same extra validation and metadata parameters as Query , Path and others you will see later.

Embed a single body parameter


Let's say you only have a single item body parameter from a Pydantic model Item .

By default, FastAPI will then expect its body directly.

But if you want it to expect a JSON with a key item and inside of it the model contents, as it does when
you declare extra body parameters, you can use the special Body parameter embed :

item: Item = Body(embed=True)

as in:

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Body, FastAPI

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):
results = {"item_id": item_id, "item": item}
return results

In this case FastAPI will expect a body like:

{
"item": {
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}
}

instead of:

{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2
}

Recap
You can add multiple body parameters to your path operation function, even though a request can only have
a single body.

But FastAPI will handle it, give you the correct data in your function, and validate and document the correct
schema in the path operation.

You can also declare singular values to be received as part of the body.

And you can instruct FastAPI to embed the body in a key even when there is only a single parameter
declared.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Body - Fields

The same way you can declare additional validation and metadata in path operation function
parameters with Query , Path and Body , you can declare validation and metadata inside of
Pydantic models using Pydantic's Field .

Import Field
First, you have to import it:

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Body, FastAPI


from pydantic import BaseModel, Field

app = FastAPI()

class Item(BaseModel):
name: str
description: str | None = Field(
default=None, title="The description of the item", max_length=300
)
price: float = Field(gt=0, description="The price must be greater than zero")
tax: float | None = None

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):
results = {"item_id": item_id, "item": item}
return results

Warning

Notice that Field is imported directly from pydantic , not from fastapi as are all the rest ( Query , Path , Body , etc).

Declare model attributes


You can then use Field with model attributes:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Body, FastAPI


from pydantic import BaseModel, Field

app = FastAPI()

class Item(BaseModel):
name: str
description: str | None = Field(
default=None, title="The description of the item", max_length=300
)
price: float = Field(gt=0, description="The price must be greater than zero")
tax: float | None = None

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]):
results = {"item_id": item_id, "item": item}
return results

Field works the same way as Query , Path and Body , it has all the same parameters, etc.

Technical Details

Actually, Query , Path and others you'll see next create objects of subclasses of a common Param class, which is itself
a subclass of Pydantic's FieldInfo class.

And Pydantic's Field returns an instance of FieldInfo as well.

Body also returns objects of a subclass of FieldInfo directly. And there are others you will see later that are
subclasses of the Body class.

Remember that when you import Query , Path , and others from fastapi , those are actually functions that return
special classes.

Tip

Notice how each model's attribute with a type, default value and Field has the same structure as a path operation
function's parameter, with Field instead of Path , Query and Body .

Add extra information

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
You can declare extra information in Field , Query , Body , etc. And it will be included in the
generated JSON Schema.

You will learn more about adding extra information later in the docs, when learning to declare
examples.

Warning

Extra keys passed to Field will also be present in the resulting OpenAPI schema for your application. As these keys
may not necessarily be part of the OpenAPI specification, some OpenAPI tools, for example the OpenAPI validator, may
not work with your generated schema.

Recap
You can use Pydantic's Field to declare extra validations and metadata for model attributes.

You can also use the extra keyword arguments to pass additional JSON Schema metadata.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Body - Nested Models

With FastAPI, you can define, validate, document, and use arbitrarily deeply nested models (thanks
to Pydantic).

List fields
You can define an attribute to be a subtype. For example, a Python list :

Python 3.10+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: list = []

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results

This will make tags be a list, although it doesn't declare the type of the elements of the list.

List fields with type parameter


But Python has a specific way to declare lists with internal types, or "type parameters":

Import typing's List

In Python 3.9 and above you can use the standard list to declare these type annotations as we'll
see below. 💡

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
But in Python versions before 3.9 (3.6 and above), you first need to import List from standard
Python's typing module:

from typing import List, Union

from fastapi import FastAPI


from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
name: str
description: Union[str, None] = None
price: float
tax: Union[float, None] = None
tags: List[str] = []

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results

Declare a list with a type parameter

To declare types that have type parameters (internal types), like list , dict , tuple :

If you are in a Python version lower than 3.9, import their equivalent version from the typing
module

Pass the internal type(s) as "type parameters" using square brackets: [ and ]

In Python 3.9 it would be:

my_list: list[str]

In versions of Python before 3.9, it would be:

from typing import List

my_list: List[str]

That's all standard Python syntax for type declarations.

Use that same standard syntax for model attributes with internal types.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
So, in our example, we can make tags be specifically a "list of strings":

Python 3.10+ Python 3.9+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: list[str] = []

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results

Set types
But then we think about it, and realize that tags shouldn't repeat, they would probably be unique
strings.

And Python has a special data type for sets of unique items, the set .

Then we can declare tags as a set of strings:

Python 3.10+ Python 3.9+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results

With this, even if you receive a request with duplicate data, it will be converted to a set of unique
items.

And whenever you output that data, even if the source had duplicates, it will be output as a set of
unique items.

And it will be annotated / documented accordingly too.

Nested Models
Each attribute of a Pydantic model has a type.

But that type can itself be another Pydantic model.

So, you can declare deeply nested JSON "objects" with specific attribute names, types and
validations.

All that, arbitrarily nested.

Define a submodel

For example, we can define an Image model:

Python 3.10+ Python 3.9+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel

app = FastAPI()

class Image(BaseModel):
url: str
name: str

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
tags: set[str] = set()
image: Image | None = None

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results

Use the submodel as a type

And then we can use it as the type of an attribute:

Python 3.10+ Python 3.9+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel

app = FastAPI()

class Image(BaseModel):
url: str
name: str

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
image: Image | None = None

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results

This would mean that FastAPI would expect a body similar to:

{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": ["rock", "metal", "bar"],
"image": {

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
}
}

Again, doing just that declaration, with FastAPI you get:

Editor support (completion, etc.), even for nested models

Data conversion

Data validation

Automatic documentation

Special types and validation


Apart from normal singular types like str , int , float , etc. you can use more complex singular
types that inherit from str .

To see all the options you have, checkout the docs for Pydantic's exotic types [↪]. You will see some
examples in the next chapter.

For example, as in the Image model we have a url field, we can declare it to be an instance of
Pydantic's HttpUrl instead of a str :

Python 3.10+ Python 3.9+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel, HttpUrl

app = FastAPI()

class Image(BaseModel):
url: HttpUrl
name: str

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
image: Image | None = None

@app.put("/items/{item_id}")

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results

The string will be checked to be a valid URL, and documented in JSON Schema / OpenAPI as such.

Attributes with lists of submodels


You can also use Pydantic models as subtypes of list , set , etc.:

Python 3.10+ Python 3.9+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel, HttpUrl

app = FastAPI()

class Image(BaseModel):
url: HttpUrl
name: str

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
images: list[Image] | None = None

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results

This will expect (convert, validate, document, etc.) a JSON body like:

{
"name": "Foo",
"description": "The pretender",
"price": 42.0,
"tax": 3.2,
"tags": [
"rock",
"metal",
"bar"
],

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
"images": [
{
"url": "http://example.com/baz.jpg",
"name": "The Foo live"
},
{
"url": "http://example.com/dave.jpg",
"name": "The Baz"
}
]
}

Info

Notice how the images key now has a list of image objects.

Deeply nested models


You can define arbitrarily deeply nested models:

Python 3.10+ Python 3.9+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel, HttpUrl

app = FastAPI()

class Image(BaseModel):
url: HttpUrl
name: str

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()
images: list[Image] | None = None

class Offer(BaseModel):
name: str
description: str | None = None
price: float
items: list[Item]

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
@app.post("/offers/")
async def create_offer(offer: Offer):
return offer

Info

Notice how Offer has a list of Item s, which in turn have an optional list of Image s

Bodies of pure lists


If the top level value of the JSON body you expect is a JSON array (a Python list ), you can
declare the type in the parameter of the function, the same as in Pydantic models:

images: List[Image]

or in Python 3.9 and above:

images: list[Image]

as in:

Python 3.9+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel, HttpUrl

app = FastAPI()

class Image(BaseModel):
url: HttpUrl
name: str

@app.post("/images/multiple/")
async def create_multiple_images(images: list[Image]):
return images

Editor support everywhere


And you get editor support everywhere.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Even for items inside of lists:

You couldn't get this kind of editor support if you were working directly with dict instead of
Pydantic models.

But you don't have to worry about them either, incoming dicts are converted automatically and your
output is converted automatically to JSON too.

Bodies of arbitrary dict s


You can also declare a body as a dict with keys of some type and values of some other type.

This way, you don't have to know beforehand what the valid field/attribute names are (as would be
the case with Pydantic models).

This would be useful if you want to receive keys that you don't already know.

Another useful case is when you want to have keys of another type (e.g., int ).

That's what we are going to see here.

In this case, you would accept any dict as long as it has int keys with float values:

Python 3.9+ Python 3.8+

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
from fastapi import FastAPI

app = FastAPI()

@app.post("/index-weights/")
async def create_index_weights(weights: dict[int, float]):
return weights

Tip

Keep in mind that JSON only supports str as keys.

But Pydantic has automatic data conversion.

This means that, even though your API clients can only send strings as keys, as long as those strings contain pure
integers, Pydantic will convert them and validate them.

And the dict you receive as weights will actually have int keys and float values.

Recap
With FastAPI you have the maximum flexibility provided by Pydantic models, while keeping your
code simple, short and elegant.

But with all the benefits:

Editor support (completion everywhere!)

Data conversion (a.k.a. parsing / serialization)

Data validation

Schema documentation

Automatic docs

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Declare Request Example Data

You can declare examples of the data your app can receive.

Here are several ways to do it.

Extra JSON Schema data in Pydantic models


You can declare examples for a Pydantic model that will be added to the generated JSON Schema.

Python 3.10+ Pydantic v2 Python 3.10+ Pydantic v1 Python 3.8+ Pydantic v2 Python 3.8+ Pydantic v1

from fastapi import FastAPI


from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None

model_config = {
"json_schema_extra": {
"examples": [
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
]
}
}

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results

That extra info will be added as-is to the output JSON Schema for that model, and it will be used in the API docs.

Pydantic v2 Pydantic v1

In Pydantic version 2, you would use the attribute model_config , that takes a dict as described in
Pydantic's docs: Model Config [↪].

You can set "json_schema_extra" with a dict containing any additional data you would like to show up in the generated JSON
Schema, including examples .

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Tip

You could use the same technique to extend the JSON Schema and add your own custom extra info.

For example you could use it to add metadata for a frontend user interface, etc.

Info

OpenAPI 3.1.0 (used since FastAPI 0.99.0) added support for examples , which is part of the JSON Schema standard.

Before that, it only supported the keyword example with a single example. That is still supported by OpenAPI 3.1.0, but is deprecated and is not part of the
JSON Schema standard. So you are encouraged to migrate example to examples . 🤓
You can read more at the end of this page.

Field additional arguments


When using Field() with Pydantic models, you can also declare additional examples :

Python 3.10+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel, Field

app = FastAPI()

class Item(BaseModel):
name: str = Field(examples=["Foo"])
description: str | None = Field(default=None, examples=["A very nice Item"])
price: float = Field(examples=[35.4])
tax: float | None = Field(default=None, examples=[3.2])

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item):
results = {"item_id": item_id, "item": item}
return results

examples in JSON Schema - OpenAPI


When using any of:

Path()

Query()

Header()

Cookie()

Body()

Form()

File()

you can also declare a group of examples with additional information that will be added to their JSON Schemas inside of
OpenAPI.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Body with examples

Here we pass examples containing one example of the data expected in Body() :

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Body, FastAPI


from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None

@app.put("/items/{item_id}")
async def update_item(
item_id: int,
item: Annotated[
Item,
Body(
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
],
),
],
):
results = {"item_id": item_id, "item": item}
return results

Example in the docs UI

With any of the methods above it would look like this in the /docs :

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Body with multiple examples

You can of course also pass multiple examples :

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Body, FastAPI


from pydantic import BaseModel

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
app = FastAPI()

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None

@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Annotated[
Item,
Body(
examples=[
{
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
{
"name": "Bar",
"price": "35.4",
},
{
"name": "Baz",
"price": "thirty five point four",
},
],
),
],
):
results = {"item_id": item_id, "item": item}
return results

When you do this, the examples will be part of the internal JSON Schema for that body data.

Nevertheless, at the time of writing this, Swagger UI, the tool in charge of showing the docs UI, doesn't support showing multiple
examples for the data in JSON Schema. But read below for a workaround.

OpenAPI-specific examples

Since before JSON Schema supported examples OpenAPI had support for a different field also called examples .

This OpenAPI-specific examples goes in another section in the OpenAPI specification. It goes in the details for each path
operation, not inside each JSON Schema.

And Swagger UI has supported this particular examples field for a while. So, you can use it to show different examples in the
docs UI.

The shape of this OpenAPI-specific field examples is a dict with multiple examples (instead of a list ), each with extra
information that will be added to OpenAPI too.

This doesn't go inside of each JSON Schema contained in OpenAPI, this goes outside, in the path operation directly.

Using the openapi_examples Parameter

You can declare the OpenAPI-specific examples in FastAPI with the parameter openapi_examples for:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Path()

Query()

Header()

Cookie()

Body()

Form()

File()

The keys of the dict identify each example, and each value is another dict .

Each specific example dict in the examples can contain:

summary : Short description for the example.

description : A long description that can contain Markdown text.

value : This is the actual example shown, e.g. a dict .

externalValue : alternative to value , a URL pointing to the example. Although this might not be supported by as many
tools as value .

You can use it like this:

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Body, FastAPI


from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None

@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Annotated[
Item,
Body(
openapi_examples={
"normal": {
"summary": "A normal example",
"description": "A **normal** item works correctly.",
"value": {
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
},
},
"converted": {
"summary": "An example with converted data",
"description": "FastAPI can convert price `strings` to actual `numbers` automatically",
"value": {

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
"name": "Bar",
"price": "35.4",
},
},
"invalid": {
"summary": "Invalid data is rejected with an error",
"value": {
"name": "Baz",
"price": "thirty five point four",
},
},
},
),
],
):
results = {"item_id": item_id, "item": item}
return results

OpenAPI Examples in the Docs UI

With openapi_examples added to Body() the /docs would look like:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Technical Details

Tip

If you are already using FastAPI version 0.99.0 or above, you can probably skip these details.

They are more relevant for older versions, before OpenAPI 3.1.0 was available.

You can consider this a brief OpenAPI and JSON Schema history lesson. 🤓

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Warning

These are very technical details about the standards JSON Schema and OpenAPI.

If the ideas above already work for you, that might be enough, and you probably don't need these details, feel free to skip them.

Before OpenAPI 3.1.0, OpenAPI used an older and modified version of JSON Schema.

JSON Schema didn't have examples , so OpenAPI added it's own example field to its own modified version.

OpenAPI also added example and examples fields to other parts of the specification:

Parameter Object (in the specification) [↪] that was used by FastAPI's:

Path()

Query()

Header()

Cookie()

Request Body Object , in the field content , on the Media Type Object (in the specification) [↪] that was used by
FastAPI's:

Body()

File()

Form()

Info

This old OpenAPI-specific examples parameter is now openapi_examples since FastAPI 0.103.0 .

JSON Schema's examples field

But then JSON Schema added an examples [↪] field to a new version of the specification.

And then the new OpenAPI 3.1.0 was based on the latest version (JSON Schema 2020-12) that included this new field examples .

And now this new examples field takes precedence over the old single (and custom) example field, that is now deprecated.

This new examples field in JSON Schema is just a list of examples, not a dict with extra metadata as in the other places in
OpenAPI (described above).

Info

Even after OpenAPI 3.1.0 was released with this new simpler integration with JSON Schema, for a while, Swagger UI, the tool that provides the automatic
docs, didn't support OpenAPI 3.1.0 (it does since version 5.0.0 🎉).
Because of that, versions of FastAPI previous to 0.99.0 still used versions of OpenAPI lower than 3.1.0.

Pydantic and FastAPI examples

When you add examples inside a Pydantic model, using schema_extra or Field(examples=["something"]) that example is
added to the JSON Schema for that Pydantic model.

And that JSON Schema of the Pydantic model is included in the OpenAPI of your API, and then it's used in the docs UI.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
In versions of FastAPI before 0.99.0 (0.99.0 and above use the newer OpenAPI 3.1.0) when you used example or examples with
any of the other utilities ( Query() , Body() , etc.) those examples were not added to the JSON Schema that describes that data
(not even to OpenAPI's own version of JSON Schema), they were added directly to the path operation declaration in OpenAPI
(outside the parts of OpenAPI that use JSON Schema).

But now that FastAPI 0.99.0 and above uses OpenAPI 3.1.0, that uses JSON Schema 2020-12, and Swagger UI 5.0.0 and above,
everything is more consistent and the examples are included in JSON Schema.

Swagger UI and OpenAPI-specific examples

Now, as Swagger UI didn't support multiple JSON Schema examples (as of 2023-08-26), users didn't have a way to show multiple
examples in the docs.

To solve that, FastAPI 0.103.0 added support for declaring the same old OpenAPI-specific examples field with the new
parameter openapi_examples . 🤓
Summary

I used to say I didn't like history that much... and look at me now giving "tech history" lessons. 😅
In short, upgrade to FastAPI 0.99.0 or above, and things are much simpler, consistent, and intuitive, and you don't have to know
all these historic details. 😎

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Extra Data Types

Up to now, you have been using common data types, like:

int

float

str

bool

But you can also use more complex data types.

And you will still have the same features as seen up to now:

Great editor support.

Data conversion from incoming requests.

Data conversion for response data.

Data validation.

Automatic annotation and documentation.

Other data types


Here are some of the additional data types you can use:

UUID :

A standard "Universally Unique Identifier", common as an ID in many databases and


systems.

In requests and responses will be represented as a str .

datetime.datetime :

A Python datetime.datetime .

In requests and responses will be represented as a str in ISO 8601 format, like:
2008-09-15T15:53:00+05:00 .

datetime.date :

Python datetime.date .

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
In requests and responses will be represented as a str in ISO 8601 format, like:
2008-09-15 .

datetime.time :

A Python datetime.time .

In requests and responses will be represented as a str in ISO 8601 format, like:
14:23:55.003 .

datetime.timedelta :

A Python datetime.timedelta .

In requests and responses will be represented as a float of total seconds.

Pydantic also allows representing it as a "ISO 8601 time diff encoding",


see the docs for more info [↪].

frozenset :

In requests and responses, treated the same as a set :

In requests, a list will be read, eliminating duplicates and converting it to a set .

In responses, the set will be converted to a list .

The generated schema will specify that the set values are unique (using JSON
Schema's uniqueItems ).

bytes :

Standard Python bytes .

In requests and responses will be treated as str .

The generated schema will specify that it's a str with binary "format".

Decimal :

Standard Python Decimal .

In requests and responses, handled the same as a float .

You can check all the valid pydantic data types here: Pydantic data types [↪].

Example
Here's an example path operation with parameters using some of the above types.

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from datetime import datetime, time, timedelta

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
from typing import Annotated
from uuid import UUID

from fastapi import Body, FastAPI

app = FastAPI()

@app.put("/items/{item_id}")
async def read_items(
item_id: UUID,
start_datetime: Annotated[datetime, Body()],
end_datetime: Annotated[datetime, Body()],
process_after: Annotated[timedelta, Body()],
repeat_at: Annotated[time | None, Body()] = None,
):
start_process = start_datetime + process_after
duration = end_datetime - start_process
return {
"item_id": item_id,
"start_datetime": start_datetime,
"end_datetime": end_datetime,
"process_after": process_after,
"repeat_at": repeat_at,
"start_process": start_process,
"duration": duration,
}

Note that the parameters inside the function have their natural data type, and you can, for example,
perform normal date manipulations, like:

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from datetime import datetime, time, timedelta


from typing import Annotated
from uuid import UUID

from fastapi import Body, FastAPI

app = FastAPI()

@app.put("/items/{item_id}")
async def read_items(
item_id: UUID,
start_datetime: Annotated[datetime, Body()],
end_datetime: Annotated[datetime, Body()],
process_after: Annotated[timedelta, Body()],
repeat_at: Annotated[time | None, Body()] = None,
):
start_process = start_datetime + process_after

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
duration = end_datetime - start_process
return {
"item_id": item_id,
"start_datetime": start_datetime,
"end_datetime": end_datetime,
"process_after": process_after,
"repeat_at": repeat_at,
"start_process": start_process,
"duration": duration,
}

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Cookie Parameters

You can define Cookie parameters the same way you define Query and Path parameters.

Import Cookie
First import Cookie :

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Cookie, FastAPI

app = FastAPI()

@app.get("/items/")
async def read_items(ads_id: Annotated[str | None, Cookie()] = None):
return {"ads_id": ads_id}

Declare Cookie parameters


Then declare the cookie parameters using the same structure as with Path and Query .

The first value is the default value, you can pass all the extra validation or annotation parameters:

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Cookie, FastAPI

app = FastAPI()

@app.get("/items/")
async def read_items(ads_id: Annotated[str | None, Cookie()] = None):
return {"ads_id": ads_id}

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Technical Details

Cookie is a "sister" class of Path and Query . It also inherits from the same common Param class.

But remember that when you import Query , Path , Cookie and others from fastapi , those are actually functions that
return special classes.

Info

To declare cookies, you need to use Cookie , because otherwise the parameters would be interpreted as query
parameters.

Recap
Declare cookies with Cookie , using the same common pattern as Query and Path .

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Header Parameters

You can define Header parameters the same way you define Query , Path and Cookie parameters.

Import Header
First import Header :

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, Header

app = FastAPI()

@app.get("/items/")
async def read_items(user_agent: Annotated[str | None, Header()] = None):
return {"User-Agent": user_agent}

Declare Header parameters


Then declare the header parameters using the same structure as with Path , Query and Cookie .

The first value is the default value, you can pass all the extra validation or annotation parameters:

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, Header

app = FastAPI()

@app.get("/items/")
async def read_items(user_agent: Annotated[str | None, Header()] = None):
return {"User-Agent": user_agent}

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Technical Details

Header is a "sister" class of Path , Query and Cookie . It also inherits from the same common Param class.

But remember that when you import Query , Path , Header , and others from fastapi , those are actually functions that
return special classes.

Info

To declare headers, you need to use Header , because otherwise the parameters would be interpreted as query parameters.

Automatic conversion
Header has a little extra functionality on top of what Path , Query and Cookie provide.

Most of the standard headers are separated by a "hyphen" character, also known as the "minus
symbol" ( - ).

But a variable like user-agent is invalid in Python.

So, by default, Header will convert the parameter names characters from underscore ( _ ) to hyphen (
- ) to extract and document the headers.

Also, HTTP headers are case-insensitive, so, you can declare them with standard Python style (also
known as "snake_case").

So, you can use user_agent as you normally would in Python code, instead of needing to capitalize
the first letters as User_Agent or something similar.

If for some reason you need to disable automatic conversion of underscores to hyphens, set the
parameter convert_underscores of Header to False :

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, Header

app = FastAPI()

@app.get("/items/")
async def read_items(
strange_header: Annotated[str | None, Header(convert_underscores=False)] = None,
):
return {"strange_header": strange_header}

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Warning

Before setting convert_underscores to False , bear in mind that some HTTP proxies and servers disallow the usage of
headers with underscores.

Duplicate headers
It is possible to receive duplicate headers. That means, the same header with multiple values.

You can define those cases using a list in the type declaration.

You will receive all the values from the duplicate header as a Python list .

For example, to declare a header of X-Token that can appear more than once, you can write:

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.9+ non-Annotated Py

from typing import Annotated

from fastapi import FastAPI, Header

app = FastAPI()

@app.get("/items/")
async def read_items(x_token: Annotated[list[str] | None, Header()] = None):
return {"X-Token values": x_token}

If you communicate with that path operation sending two HTTP headers like:

X-Token: foo
X-Token: bar

The response would be like:

{
"X-Token values": [
"bar",
"foo"
]
}

Recap
Declare headers with Header , using the same common pattern as Query , Path and Cookie .

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
And don't worry about underscores in your variables, FastAPI will take care of converting them.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Response Model - Return Type

You can declare the type used for the response by annotating the path operation function return type.

You can use type annotations the same way you would for input data in function parameters, you can use
Pydantic models, lists, dictionaries, scalar values like integers, booleans, etc.

Python 3.10+ Python 3.9+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: list[str] = []

@app.post("/items/")
async def create_item(item: Item) -> Item:
return item

@app.get("/items/")
async def read_items() -> list[Item]:
return [
Item(name="Portal Gun", price=42.0),
Item(name="Plumbus", price=32.0),
]

FastAPI will use this return type to:

Validate the returned data.

If the data is invalid (e.g. you are missing a field), it means that your app code is broken, not
returning what it should, and it will return a server error instead of returning incorrect data. This
way you and your clients can be certain that they will receive the data and the data shape
expected.

Add a JSON Schema for the response, in the OpenAPI path operation.

This will be used by the automatic docs.

It will also be used by automatic client code generation tools.

But most importantly:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
It will limit and filter the output data to what is defined in the return type.

This is particularly important for security, we'll see more of that below.

response_model Parameter
There are some cases where you need or want to return some data that is not exactly what the type
declares.

For example, you could want to return a dictionary or a database object, but declare it as a Pydantic
model. This way the Pydantic model would do all the data documentation, validation, etc. for the object
that you returned (e.g. a dictionary or database object).

If you added the return type annotation, tools and editors would complain with a (correct) error telling you
that your function is returning a type (e.g. a dict) that is different from what you declared (e.g. a Pydantic
model).

In those cases, you can use the path operation decorator parameter response_model instead of the return
type.

You can use the response_model parameter in any of the path operations:

@app.get()

@app.post()

@app.put()

@app.delete()

etc.

Python 3.10+ Python 3.9+ Python 3.8+

from typing import Any

from fastapi import FastAPI


from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: list[str] = []

@app.post("/items/", response_model=Item)

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
async def create_item(item: Item) -> Any:
return item

@app.get("/items/", response_model=list[Item])
async def read_items() -> Any:
return [
{"name": "Portal Gun", "price": 42.0},
{"name": "Plumbus", "price": 32.0},
]

Note

Notice that response_model is a parameter of the "decorator" method ( get , post , etc). Not of your path operation function, like
all the parameters and body.

response_model receives the same type you would declare for a Pydantic model field, so, it can be a
Pydantic model, but it can also be, e.g. a list of Pydantic models, like List[Item] .

FastAPI will use this response_model to do all the data documentation, validation, etc. and also to convert
and filter the output data to its type declaration.

Tip

If you have strict type checks in your editor, mypy, etc, you can declare the function return type as Any .

That way you tell the editor that you are intentionally returning anything. But FastAPI will still do the data documentation,
validation, filtering, etc. with the response_model .

response_model Priority

If you declare both a return type and a response_model , the response_model will take priority and be used
by FastAPI.

This way you can add correct type annotations to your functions even when you are returning a type
different than the response model, to be used by the editor and tools like mypy. And still you can have
FastAPI do the data validation, documentation, etc. using the response_model .

You can also use response_model=None to disable creating a response model for that path operation, you
might need to do it if you are adding type annotations for things that are not valid Pydantic fields, you will
see an example of that in one of the sections below.

Return the same input data


Here we are declaring a UserIn model, it will contain a plaintext password:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Python 3.10+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel, EmailStr

app = FastAPI()

class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: str | None = None

# Don't do this in production!


@app.post("/user/")
async def create_user(user: UserIn) -> UserIn:
return user

Info

To use EmailStr , first install email_validator [↪].

E.g. pip install email-validator or pip install pydantic[email] .

And we are using this model to declare our input and the same model to declare our output:

Python 3.10+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel, EmailStr

app = FastAPI()

class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: str | None = None

# Don't do this in production!


@app.post("/user/")
async def create_user(user: UserIn) -> UserIn:
return user

Now, whenever a browser is creating a user with a password, the API will return the same password in the
response.

In this case, it might not be a problem, because it's the same user sending the password.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
But if we use the same model for another path operation, we could be sending our user's passwords to
every client.

Danger

Never store the plain password of a user or send it in a response like this, unless you know all the caveats and you know what you
are doing.

Add an output model


We can instead create an input model with the plaintext password and an output model without it:

Python 3.10+ Python 3.8+

from typing import Any

from fastapi import FastAPI


from pydantic import BaseModel, EmailStr

app = FastAPI()

class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: str | None = None

class UserOut(BaseModel):
username: str
email: EmailStr
full_name: str | None = None

@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
return user

Here, even though our path operation function is returning the same input user that contains the password:

Python 3.10+ Python 3.8+

from typing import Any

from fastapi import FastAPI


from pydantic import BaseModel, EmailStr

app = FastAPI()

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: str | None = None

class UserOut(BaseModel):
username: str
email: EmailStr
full_name: str | None = None

@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
return user

...we declared the response_model to be our model UserOut , that doesn't include the password:

Python 3.10+ Python 3.8+

from typing import Any

from fastapi import FastAPI


from pydantic import BaseModel, EmailStr

app = FastAPI()

class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: str | None = None

class UserOut(BaseModel):
username: str
email: EmailStr
full_name: str | None = None

@app.post("/user/", response_model=UserOut)
async def create_user(user: UserIn) -> Any:
return user

So, FastAPI will take care of filtering out all the data that is not declared in the output model (using
Pydantic).

response_model or Return Type

In this case, because the two models are different, if we annotated the function return type as UserOut ,
the editor and tools would complain that we are returning an invalid type, as those are different classes.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
That's why in this example we have to declare it in the response_model parameter.

...but continue reading below to see how to overcome that.

Return Type and Data Filtering


Let's continue from the previous example. We wanted to annotate the function with one type but return
something that includes more data.

We want FastAPI to keep filtering the data using the response model.

In the previous example, because the classes were different, we had to use the response_model
parameter. But that also means that we don't get the support from the editor and tools checking the
function return type.

But in most of the cases where we need to do something like this, we want the model just to filter/remove
some of the data as in this example.

And in those cases, we can use classes and inheritance to take advantage of function type annotations to
get better support in the editor and tools, and still get the FastAPI data filtering.

Python 3.10+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel, EmailStr

app = FastAPI()

class BaseUser(BaseModel):
username: str
email: EmailStr
full_name: str | None = None

class UserIn(BaseUser):
password: str

@app.post("/user/")
async def create_user(user: UserIn) -> BaseUser:
return user

With this, we get tooling support, from editors and mypy as this code is correct in terms of types, but we
also get the data filtering from FastAPI.

How does this work? Let's check that out. 🤓


Type Annotations and Tooling
Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
First let's see how editors, mypy and other tools would see this.

BaseUser has the base fields. Then UserIn inherits from BaseUser and adds the password field, so, it
will include all the fields from both models.

We annotate the function return type as BaseUser , but we are actually returning a UserIn instance.

The editor, mypy, and other tools won't complain about this because, in typing terms, UserIn is a subclass
of BaseUser , which means it's a valid type when what is expected is anything that is a BaseUser .

FastAPI Data Filtering

Now, for FastAPI, it will see the return type and make sure that what you return includes only the fields that
are declared in the type.

FastAPI does several things internally with Pydantic to make sure that those same rules of class
inheritance are not used for the returned data filtering, otherwise you could end up returning much more
data than what you expected.

This way, you can get the best of both worlds: type annotations with tooling support and data filtering.

See it in the docs


When you see the automatic docs, you can check that the input model and output model will both have
their own JSON Schema:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
And both models will be used for the interactive API documentation:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Other Return Type Annotations
There might be cases where you return something that is not a valid Pydantic field and you annotate it in
the function, only to get the support provided by tooling (the editor, mypy, etc).

Return a Response Directly

The most common case would be returning a Response directly as explained later in the advanced docs ↪.

from fastapi import FastAPI, Response


from fastapi.responses import JSONResponse, RedirectResponse

app = FastAPI()

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
@app.get("/portal")
async def get_portal(teleport: bool = False) -> Response:
if teleport:
return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
return JSONResponse(content={"message": "Here's your interdimensional portal."})

This simple case is handled automatically by FastAPI because the return type annotation is the class (or a
subclass) of Response .

And tools will also be happy because both RedirectResponse and JSONResponse are subclasses of
Response , so the type annotation is correct.

Annotate a Response Subclass

You can also use a subclass of Response in the type annotation:

from fastapi import FastAPI


from fastapi.responses import RedirectResponse

app = FastAPI()

@app.get("/teleport")
async def get_teleport() -> RedirectResponse:
return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")

This will also work because RedirectResponse is a subclass of Response , and FastAPI will automatically
handle this simple case.

Invalid Return Type Annotations

But when you return some other arbitrary object that is not a valid Pydantic type (e.g. a database object)
and you annotate it like that in the function, FastAPI will try to create a Pydantic response model from that
type annotation, and will fail.

The same would happen if you had something like a union between different types where one or more of
them are not valid Pydantic types, for example this would fail 💥:
Python 3.10+ Python 3.8+

from fastapi import FastAPI, Response


from fastapi.responses import RedirectResponse

app = FastAPI()

@app.get("/portal")
async def get_portal(teleport: bool = False) -> Response | dict:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
if teleport:
return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
return {"message": "Here's your interdimensional portal."}

...this fails because the type annotation is not a Pydantic type and is not just a single Response class or
subclass, it's a union (any of the two) between a Response and a dict .

Disable Response Model

Continuing from the example above, you might not want to have the default data validation, documentation,
filtering, etc. that is performed by FastAPI.

But you might want to still keep the return type annotation in the function to get the support from tools like
editors and type checkers (e.g. mypy).

In this case, you can disable the response model generation by setting response_model=None :

Python 3.10+ Python 3.8+

from fastapi import FastAPI, Response


from fastapi.responses import RedirectResponse

app = FastAPI()

@app.get("/portal", response_model=None)
async def get_portal(teleport: bool = False) -> Response | dict:
if teleport:
return RedirectResponse(url="https://www.youtube.com/watch?v=dQw4w9WgXcQ")
return {"message": "Here's your interdimensional portal."}

This will make FastAPI skip the response model generation and that way you can have any return type
annotations you need without it affecting your FastAPI application. 🤓
Response Model encoding parameters
Your response model could have default values, like:

Python 3.10+ Python 3.9+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
name: str
description: str | None = None

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
price: float
tax: float = 10.5
tags: list[str] = []

items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}

@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)


async def read_item(item_id: str):
return items[item_id]

description: Union[str, None] = None (or str | None = None in Python 3.10) has a default of
None .

tax: float = 10.5 has a default of 10.5 .

tags: List[str] = [] has a default of an empty list: [] .

but you might want to omit them from the result if they were not actually stored.

For example, if you have models with many optional attributes in a NoSQL database, but you don't want to
send very long JSON responses full of default values.

Use the response_model_exclude_unset parameter

You can set the path operation decorator parameter response_model_exclude_unset=True :

Python 3.10+ Python 3.9+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float = 10.5
tags: list[str] = []

items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
@app.get("/items/{item_id}", response_model=Item, response_model_exclude_unset=True)
async def read_item(item_id: str):
return items[item_id]

and those default values won't be included in the response, only the values actually set.

So, if you send a request to that path operation for the item with ID foo , the response (not including default
values) will be:

{
"name": "Foo",
"price": 50.2
}

Info

In Pydantic v1 the method was called .dict() , it was deprecated (but still supported) in Pydantic v2, and renamed to
.model_dump() .

The examples here use .dict() for compatibility with Pydantic v1, but you should use .model_dump() instead if you can use
Pydantic v2.

Info

FastAPI uses Pydantic model's .dict() with its exclude_unset parameter [↪] to achieve this.

Info

You can also use:

response_model_exclude_defaults=True

response_model_exclude_none=True

as described in the Pydantic docs [↪] for exclude_defaults and exclude_none .

Data with values for fields with defaults

But if your data has values for the model's fields with default values, like the item with ID bar :

{
"name": "Bar",
"description": "The bartenders",
"price": 62,
"tax": 20.2
}

they will be included in the response.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Data with the same values as the defaults

If the data has the same values as the default ones, like the item with ID baz :

{
"name": "Baz",
"description": None,
"price": 50.2,
"tax": 10.5,
"tags": []
}

FastAPI is smart enough (actually, Pydantic is smart enough) to realize that, even though description ,
tax , and tags have the same values as the defaults, they were set explicitly (instead of taken from the
defaults).

So, they will be included in the JSON response.

Tip

Notice that the default values can be anything, not only None .

They can be a list ( [] ), a float of 10.5 , etc.

response_model_include and response_model_exclude

You can also use the path operation decorator parameters response_model_include and
response_model_exclude .

They take a set of str with the name of the attributes to include (omitting the rest) or to exclude
(including the rest).

This can be used as a quick shortcut if you have only one Pydantic model and want to remove some data
from the output.

Tip

But it is still recommended to use the ideas above, using multiple classes, instead of these parameters.

This is because the JSON Schema generated in your app's OpenAPI (and the docs) will still be the one for the complete model,
even if you use response_model_include or response_model_exclude to omit some attributes.

This also applies to response_model_by_alias that works similarly.

Python 3.10+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
app = FastAPI()

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float = 10.5

items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
"baz": {
"name": "Baz",
"description": "There goes my baz",
"price": 50.2,
"tax": 10.5,
},
}

@app.get(
"/items/{item_id}/name",
response_model=Item,
response_model_include={"name", "description"},
)
async def read_item_name(item_id: str):
return items[item_id]

@app.get("/items/{item_id}/public", response_model=Item, response_model_exclude={"tax"})


async def read_item_public_data(item_id: str):
return items[item_id]

Tip

The syntax {"name", "description"} creates a set with those two values.

It is equivalent to set(["name", "description"]) .

Using list s instead of set s

If you forget to use a set and use a list or tuple instead, FastAPI will still convert it to a set and it will
work correctly:

Python 3.10+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel

app = FastAPI()

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float = 10.5

items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
"baz": {
"name": "Baz",
"description": "There goes my baz",
"price": 50.2,
"tax": 10.5,
},
}

@app.get(
"/items/{item_id}/name",
response_model=Item,
response_model_include=["name", "description"],
)
async def read_item_name(item_id: str):
return items[item_id]

@app.get("/items/{item_id}/public", response_model=Item, response_model_exclude=["tax"])


async def read_item_public_data(item_id: str):
return items[item_id]

Recap
Use the path operation decorator's parameter response_model to define response models and especially
to ensure private data is filtered out.

Use response_model_exclude_unset to return only the values explicitly set.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Extra Models

Continuing with the previous example, it will be common to have more than one related model.

This is especially the case for user models, because:

The input model needs to be able to have a password.

The output model should not have a password.

The database model would probably need to have a hashed password.

Danger

Never store user's plaintext passwords. Always store a "secure hash" that you can then verify.

If you don't know, you will learn what a "password hash" is in the security chapters ↪.

Multiple models
Here's a general idea of how the models could look like with their password fields and the places
where they are used:

Python 3.10+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel, EmailStr

app = FastAPI()

class UserIn(BaseModel):
username: str
password: str
email: EmailStr
full_name: str | None = None

class UserOut(BaseModel):
username: str
email: EmailStr
full_name: str | None = None

class UserInDB(BaseModel):
username: str

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
hashed_password: str
email: EmailStr
full_name: str | None = None

def fake_password_hasher(raw_password: str):


return "supersecret" + raw_password

def fake_save_user(user_in: UserIn):


hashed_password = fake_password_hasher(user_in.password)
user_in_db = UserInDB(**user_in.dict(), hashed_password=hashed_password)
print("User saved! ..not really")
return user_in_db

@app.post("/user/", response_model=UserOut)
async def create_user(user_in: UserIn):
user_saved = fake_save_user(user_in)
return user_saved

Info

In Pydantic v1 the method was called .dict() , it was deprecated (but still supported) in Pydantic v2, and renamed to
.model_dump() .

The examples here use .dict() for compatibility with Pydantic v1, but you should use .model_dump() instead if you
can use Pydantic v2.

About **user_in.dict()

Pydantic's .dict()

user_in is a Pydantic model of class UserIn .

Pydantic models have a .dict() method that returns a dict with the model's data.

So, if we create a Pydantic object user_in like:

user_in = UserIn(username="john", password="secret", email="[email protected]")

and then we call:

user_dict = user_in.dict()

we now have a dict with the data in the variable user_dict (it's a dict instead of a Pydantic
model object).

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
And if we call:

print(user_dict)

we would get a Python dict with:

{
'username': 'john',
'password': 'secret',
'email': '[email protected]',
'full_name': None,
}

Unwrapping a dict

If we take a dict like user_dict and pass it to a function (or class) with **user_dict , Python will
"unwrap" it. It will pass the keys and values of the user_dict directly as key-value arguments.

So, continuing with the user_dict from above, writing:

UserInDB(**user_dict)

would result in something equivalent to:

UserInDB(
username="john",
password="secret",
email="[email protected]",
full_name=None,
)

Or more exactly, using user_dict directly, with whatever contents it might have in the future:

UserInDB(
username = user_dict["username"],
password = user_dict["password"],
email = user_dict["email"],
full_name = user_dict["full_name"],
)

A Pydantic model from the contents of another

As in the example above we got user_dict from user_in.dict() , this code:

user_dict = user_in.dict()

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
UserInDB(**user_dict)

would be equivalent to:

UserInDB(**user_in.dict())

...because user_in.dict() is a dict , and then we make Python "unwrap" it by passing it to


UserInDB prefixed with ** .

So, we get a Pydantic model from the data in another Pydantic model.

Unwrapping a dict and extra keywords

And then adding the extra keyword argument hashed_password=hashed_password , like in:

UserInDB(**user_in.dict(), hashed_password=hashed_password)

...ends up being like:

UserInDB(
username = user_dict["username"],
password = user_dict["password"],
email = user_dict["email"],
full_name = user_dict["full_name"],
hashed_password = hashed_password,
)

Warning

The supporting additional functions are just to demo a possible flow of the data, but they of course are not providing any
real security.

Reduce duplication
Reducing code duplication is one of the core ideas in FastAPI.

As code duplication increments the chances of bugs, security issues, code desynchronization
issues (when you update in one place but not in the others), etc.

And these models are all sharing a lot of the data and duplicating attribute names and types.

We could do better.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
We can declare a UserBase model that serves as a base for our other models. And then we can
make subclasses of that model that inherit its attributes (type declarations, validation, etc).

All the data conversion, validation, documentation, etc. will still work as normally.

That way, we can declare just the differences between the models (with plaintext password , with
hashed_password and without password):

Python 3.10+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel, EmailStr

app = FastAPI()

class UserBase(BaseModel):
username: str
email: EmailStr
full_name: str | None = None

class UserIn(UserBase):
password: str

class UserOut(UserBase):
pass

class UserInDB(UserBase):
hashed_password: str

def fake_password_hasher(raw_password: str):


return "supersecret" + raw_password

def fake_save_user(user_in: UserIn):


hashed_password = fake_password_hasher(user_in.password)
user_in_db = UserInDB(**user_in.dict(), hashed_password=hashed_password)
print("User saved! ..not really")
return user_in_db

@app.post("/user/", response_model=UserOut)
async def create_user(user_in: UserIn):
user_saved = fake_save_user(user_in)
return user_saved

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Union or anyOf
You can declare a response to be the Union of two types, that means, that the response would be
any of the two.

It will be defined in OpenAPI with anyOf .

To do that, use the standard Python type hint typing.Union [↪]:

Note

When defining a Union [↪], include the most specific type first, followed by the less specific type. In the example below,
the more specific PlaneItem comes before CarItem in Union[PlaneItem, CarItem] .

Python 3.10+ Python 3.8+

from typing import Union

from fastapi import FastAPI


from pydantic import BaseModel

app = FastAPI()

class BaseItem(BaseModel):
description: str
type: str

class CarItem(BaseItem):
type: str = "car"

class PlaneItem(BaseItem):
type: str = "plane"
size: int

items = {
"item1": {"description": "All my friends drive a low rider", "type": "car"},
"item2": {
"description": "Music is my aeroplane, it's my aeroplane",
"type": "plane",
"size": 5,
},
}

@app.get("/items/{item_id}", response_model=Union[PlaneItem, CarItem])

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
async def read_item(item_id: str):
return items[item_id]

Union in Python 3.10

In this example we pass Union[PlaneItem, CarItem] as the value of the argument


response_model .

Because we are passing it as a value to an argument instead of putting it in a type annotation, we


have to use Union even in Python 3.10.

If it was in a type annotation we could have used the vertical bar, as:

some_variable: PlaneItem | CarItem

But if we put that in response_model=PlaneItem | CarItem we would get an error, because Python
would try to perform an invalid operation between PlaneItem and CarItem instead of interpreting
that as a type annotation.

List of models
The same way, you can declare responses of lists of objects.

For that, use the standard Python typing.List (or just list in Python 3.9 and above):

Python 3.9+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
name: str
description: str

items = [
{"name": "Foo", "description": "There comes my hero"},
{"name": "Red", "description": "It's my aeroplane"},
]

@app.get("/items/", response_model=list[Item])

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
async def read_items():
return items

Response with arbitrary dict


You can also declare a response using a plain arbitrary dict , declaring just the type of the keys and
values, without using a Pydantic model.

This is useful if you don't know the valid field/attribute names (that would be needed for a Pydantic
model) beforehand.

In this case, you can use typing.Dict (or just dict in Python 3.9 and above):

Python 3.9+ Python 3.8+

from fastapi import FastAPI

app = FastAPI()

@app.get("/keyword-weights/", response_model=dict[str, float])


async def read_keyword_weights():
return {"foo": 2.3, "bar": 3.4}

Recap
Use multiple Pydantic models and inherit freely for each case.

You don't need to have a single data model per entity if that entity must be able to have different
"states". As the case with the user "entity" with a state including password , password_hash and no
password.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Response Status Code

The same way you can specify a response model, you can also declare the HTTP status code used
for the response with the parameter status_code in any of the path operations:

@app.get()

@app.post()

@app.put()

@app.delete()

etc.

from fastapi import FastAPI

app = FastAPI()

@app.post("/items/", status_code=201)
async def create_item(name: str):
return {"name": name}

Note

Notice that status_code is a parameter of the "decorator" method ( get , post , etc). Not of your path operation function,
like all the parameters and body.

The status_code parameter receives a number with the HTTP status code.

Info

status_code can alternatively also receive an IntEnum , such as Python's http.HTTPStatus [↪].

It will:

Return that status code in the response.

Document it as such in the OpenAPI schema (and so, in the user interfaces):

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Note

Some response codes (see the next section) indicate that the response does not have a body.

FastAPI knows this, and will produce OpenAPI docs that state there is no response body.

About HTTP status codes


Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Note

If you already know what HTTP status codes are, skip to the next section.

In HTTP, you send a numeric status code of 3 digits as part of the response.

These status codes have a name associated to recognize them, but the important part is the
number.

In short:

100 and above are for "Information". You rarely use them directly. Responses with these status
codes cannot have a body.

200 and above are for "Successful" responses. These are the ones you would use the most.

200 is the default status code, which means everything was "OK".

Another example would be 201 , "Created". It is commonly used after creating a new record
in the database.

A special case is 204 , "No Content". This response is used when there is no content to
return to the client, and so the response must not have a body.

300 and above are for "Redirection". Responses with these status codes may or may not have a
body, except for 304 , "Not Modified", which must not have one.

400 and above are for "Client error" responses. These are the second type you would probably
use the most.

An example is 404 , for a "Not Found" response.

For generic errors from the client, you can just use 400 .

500 and above are for server errors. You almost never use them directly. When something goes
wrong at some part in your application code, or server, it will automatically return one of these
status codes.

Tip

To know more about each status code and which code is for what, check the
MDN documentation about HTTP status codes [↪].

Shortcut to remember the names


Let's see the previous example again:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
from fastapi import FastAPI

app = FastAPI()

@app.post("/items/", status_code=201)
async def create_item(name: str):
return {"name": name}

201 is the status code for "Created".

But you don't have to memorize what each of these codes mean.

You can use the convenience variables from fastapi.status .

from fastapi import FastAPI, status

app = FastAPI()

@app.post("/items/", status_code=status.HTTP_201_CREATED)
async def create_item(name: str):
return {"name": name}

They are just a convenience, they hold the same number, but that way you can use the editor's
autocomplete to find them:

Technical Details

You could also use from starlette import status .

FastAPI provides the same starlette.status as fastapi.status just as a convenience for you, the developer. But it
comes directly from Starlette.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Changing the default
Later, in the Advanced User Guide ↪, you will see how to return a different status code than the
default you are declaring here.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Form Data

When you need to receive form fields instead of JSON, you can use Form .

Info

To use forms, first install python-multipart [↪].

E.g. pip install python-multipart .

Import Form
Import Form from fastapi :

Python 3.9+ Python 3.8+ Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, Form

app = FastAPI()

@app.post("/login/")
async def login(username: Annotated[str, Form()], password: Annotated[str, Form()]):
return {"username": username}

Define Form parameters


Create form parameters the same way you would for Body or Query :

Python 3.9+ Python 3.8+ Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, Form

app = FastAPI()

@app.post("/login/")
async def login(username: Annotated[str, Form()], password: Annotated[str, Form()]):
return {"username": username}

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
For example, in one of the ways the OAuth2 specification can be used (called "password flow") it is
required to send a username and password as form fields.

The spec requires the fields to be exactly named username and password , and to be sent as form
fields, not JSON.

With Form you can declare the same configurations as with Body (and Query , Path , Cookie ),
including validation, examples, an alias (e.g. user-name instead of username ), etc.

Info

Form is a class that inherits directly from Body .

Tip

To declare form bodies, you need to use Form explicitly, because without it the parameters would be interpreted as query
parameters or body (JSON) parameters.

About "Form Fields"


The way HTML forms ( <form></form> ) sends the data to the server normally uses a "special"
encoding for that data, it's different from JSON.

FastAPI will make sure to read that data from the right place instead of JSON.

Technical Details

Data from forms is normally encoded using the "media type" application/x-www-form-urlencoded .

But when the form includes files, it is encoded as multipart/form-data . You'll read about handling files in the next
chapter.

If you want to read more about these encodings and form fields, head to the MDN web docs for POST [↪].

Warning

You can declare multiple Form parameters in a path operation, but you can't also declare Body fields that you expect to
receive as JSON, as the request will have the body encoded using application/x-www-form-urlencoded instead of
application/json .

This is not a limitation of FastAPI, it's part of the HTTP protocol.

Recap
Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Use Form to declare form data input parameters.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Request Files

You can define files to be uploaded by the client using File .

Info

To receive uploaded files, first install python-multipart [↪].

E.g. pip install python-multipart .

This is because uploaded files are sent as "form data".

Import File
Import File and UploadFile from fastapi :

Python 3.9+ Python 3.8+ Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, File, UploadFile

app = FastAPI()

@app.post("/files/")
async def create_file(file: Annotated[bytes, File()]):
return {"file_size": len(file)}

@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
return {"filename": file.filename}

Define File Parameters


Create file parameters the same way you would for Body or Form :

Python 3.9+ Python 3.8+ Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, File, UploadFile

app = FastAPI()

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
@app.post("/files/")
async def create_file(file: Annotated[bytes, File()]):
return {"file_size": len(file)}

@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
return {"filename": file.filename}

Info

File is a class that inherits directly from Form .

But remember that when you import Query , Path , File and others from fastapi , those are actually functions that return
special classes.

Tip

To declare File bodies, you need to use File , because otherwise the parameters would be interpreted as query parameters or
body (JSON) parameters.

The files will be uploaded as "form data".

If you declare the type of your path operation function parameter as bytes , FastAPI will read the file for
you and you will receive the contents as bytes .

Keep in mind that this means that the whole contents will be stored in memory. This will work well for small
files.

But there are several cases in which you might benefit from using UploadFile .

File Parameters with UploadFile


Define a file parameter with a type of UploadFile :

Python 3.9+ Python 3.8+ Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, File, UploadFile

app = FastAPI()

@app.post("/files/")
async def create_file(file: Annotated[bytes, File()]):
return {"file_size": len(file)}

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
return {"filename": file.filename}

Using UploadFile has several advantages over bytes :

You don't have to use File() in the default value of the parameter.

It uses a "spooled" file:

A file stored in memory up to a maximum size limit, and after passing this limit it will be stored in
disk.

This means that it will work well for large files like images, videos, large binaries, etc. without
consuming all the memory.

You can get metadata from the uploaded file.

It has a file-like [↪] async interface.

It exposes an actual Python SpooledTemporaryFile [↪] object that you can pass directly to other
libraries that expect a file-like object.

UploadFile

UploadFile has the following attributes:

filename : A str with the original file name that was uploaded (e.g. myimage.jpg ).

content_type : A str with the content type (MIME type / media type) (e.g. image/jpeg ).

file : A SpooledTemporaryFile [↪] (a file-like [↪] object). This is the actual Python file that you can
pass directly to other functions or libraries that expect a "file-like" object.

UploadFile has the following async methods. They all call the corresponding file methods underneath
(using the internal SpooledTemporaryFile ).

write(data) : Writes data ( str or bytes ) to the file.

read(size) : Reads size ( int ) bytes/characters of the file.

seek(offset) : Goes to the byte position offset ( int ) in the file.

E.g., await myfile.seek(0) would go to the start of the file.

This is especially useful if you run await myfile.read() once and then need to read the contents
again.

close() : Closes the file.

As all these methods are async methods, you need to "await" them.

For example, inside of an async path operation function you can get the contents with:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
contents = await myfile.read()

If you are inside of a normal def path operation function, you can access the UploadFile.file directly,
for example:

contents = myfile.file.read()

async Technical Details

When you use the async methods, FastAPI runs the file methods in a threadpool and awaits for them.

Starlette Technical Details

FastAPI's UploadFile inherits directly from Starlette's UploadFile , but adds some necessary parts to make it compatible with
Pydantic and the other parts of FastAPI.

What is "Form Data"


The way HTML forms ( <form></form> ) sends the data to the server normally uses a "special" encoding for
that data, it's different from JSON.

FastAPI will make sure to read that data from the right place instead of JSON.

Technical Details

Data from forms is normally encoded using the "media type" application/x-www-form-urlencoded when it doesn't include files.

But when the form includes files, it is encoded as multipart/form-data . If you use File , FastAPI will know it has to get the
files from the correct part of the body.

If you want to read more about these encodings and form fields, head to the MDN web docs for POST [↪].

Warning

You can declare multiple File and Form parameters in a path operation, but you can't also declare Body fields that you expect
to receive as JSON, as the request will have the body encoded using multipart/form-data instead of application/json .

This is not a limitation of FastAPI, it's part of the HTTP protocol.

Optional File Upload


You can make a file optional by using standard type annotations and setting a default value of None :

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, File, UploadFile

app = FastAPI()

@app.post("/files/")
async def create_file(file: Annotated[bytes | None, File()] = None):
if not file:
return {"message": "No file sent"}
else:
return {"file_size": len(file)}

@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile | None = None):
if not file:
return {"message": "No upload file sent"}
else:
return {"filename": file.filename}

UploadFile with Additional Metadata


You can also use File() with UploadFile , for example, to set additional metadata:

Python 3.9+ Python 3.8+ Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, File, UploadFile

app = FastAPI()

@app.post("/files/")
async def create_file(file: Annotated[bytes, File(description="A file read as bytes")]):
return {"file_size": len(file)}

@app.post("/uploadfile/")
async def create_upload_file(
file: Annotated[UploadFile, File(description="A file read as UploadFile")],
):
return {"filename": file.filename}

Multiple File Uploads

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
It's possible to upload several files at the same time.

They would be associated to the same "form field" sent using "form data".

To use that, declare a list of bytes or UploadFile :

Python 3.9+ Python 3.8+ Python 3.9+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, File, UploadFile


from fastapi.responses import HTMLResponse

app = FastAPI()

@app.post("/files/")
async def create_files(files: Annotated[list[bytes], File()]):
return {"file_sizes": [len(file) for file in files]}

@app.post("/uploadfiles/")
async def create_upload_files(files: list[UploadFile]):
return {"filenames": [file.filename for file in files]}

@app.get("/")
async def main():
content = """
<body>
<form action="/files/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
</body>
"""
return HTMLResponse(content=content)

You will receive, as declared, a list of bytes or UploadFile s.

Technical Details

You could also use from starlette.responses import HTMLResponse .

FastAPI provides the same starlette.responses as fastapi.responses just as a convenience for you, the developer. But most
of the available responses come directly from Starlette.

Multiple File Uploads with Additional Metadata

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
And the same way as before, you can use File() to set additional parameters, even for UploadFile :

Python 3.9+ Python 3.8+ Python 3.9+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, File, UploadFile


from fastapi.responses import HTMLResponse

app = FastAPI()

@app.post("/files/")
async def create_files(
files: Annotated[list[bytes], File(description="Multiple files as bytes")],
):
return {"file_sizes": [len(file) for file in files]}

@app.post("/uploadfiles/")
async def create_upload_files(
files: Annotated[
list[UploadFile], File(description="Multiple files as UploadFile")
],
):
return {"filenames": [file.filename for file in files]}

@app.get("/")
async def main():
content = """
<body>
<form action="/files/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
</body>
"""
return HTMLResponse(content=content)

Recap
Use File , bytes , and UploadFile to declare files to be uploaded in the request, sent as form data.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Request Forms and Files

You can define files and form fields at the same time using File and Form .

Info

To receive uploaded files and/or form data, first install python-multipart [↪].

E.g. pip install python-multipart .

Import File and Form


Python 3.9+ Python 3.8+ Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, File, Form, UploadFile

app = FastAPI()

@app.post("/files/")
async def create_file(
file: Annotated[bytes, File()],
fileb: Annotated[UploadFile, File()],
token: Annotated[str, Form()],
):
return {
"file_size": len(file),
"token": token,
"fileb_content_type": fileb.content_type,
}

Define File and Form parameters


Create file and form parameters the same way you would for Body or Query :

Python 3.9+ Python 3.8+ Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, File, Form, UploadFile

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
app = FastAPI()

@app.post("/files/")
async def create_file(
file: Annotated[bytes, File()],
fileb: Annotated[UploadFile, File()],
token: Annotated[str, Form()],
):
return {
"file_size": len(file),
"token": token,
"fileb_content_type": fileb.content_type,
}

The files and form fields will be uploaded as form data and you will receive the files and form fields.

And you can declare some of the files as bytes and some as UploadFile .

Warning

You can declare multiple File and Form parameters in a path operation, but you can't also declare Body fields that you
expect to receive as JSON, as the request will have the body encoded using multipart/form-data instead of
application/json .

This is not a limitation of FastAPI, it's part of the HTTP protocol.

Recap
Use File and Form together when you need to receive data and files in the same request.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Handling Errors

There are many situations in which you need to notify an error to a client that is using your API.

This client could be a browser with a frontend, a code from someone else, an IoT device, etc.

You could need to tell the client that:

The client doesn't have enough privileges for that operation.

The client doesn't have access to that resource.

The item the client was trying to access doesn't exist.

etc.

In these cases, you would normally return an HTTP status code in the range of 400 (from 400 to 499).

This is similar to the 200 HTTP status codes (from 200 to 299). Those "200" status codes mean that
somehow there was a "success" in the request.

The status codes in the 400 range mean that there was an error from the client.

Remember all those "404 Not Found" errors (and jokes)?

Use HTTPException
To return HTTP responses with errors to the client you use HTTPException .

Import HTTPException

from fastapi import FastAPI, HTTPException

app = FastAPI()

items = {"foo": "The Foo Wrestlers"}

@app.get("/items/{item_id}")
async def read_item(item_id: str):
if item_id not in items:
raise HTTPException(status_code=404, detail="Item not found")
return {"item": items[item_id]}

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Raise an HTTPException in your code

HTTPException is a normal Python exception with additional data relevant for APIs.

Because it's a Python exception, you don't return it, you raise it.

This also means that if you are inside a utility function that you are calling inside of your path operation
function, and you raise the HTTPException from inside of that utility function, it won't run the rest of
the code in the path operation function, it will terminate that request right away and send the HTTP
error from the HTTPException to the client.

The benefit of raising an exception over return ing a value will be more evident in the section about
Dependencies and Security.

In this example, when the client requests an item by an ID that doesn't exist, raise an exception with a
status code of 404 :

from fastapi import FastAPI, HTTPException

app = FastAPI()

items = {"foo": "The Foo Wrestlers"}

@app.get("/items/{item_id}")
async def read_item(item_id: str):
if item_id not in items:
raise HTTPException(status_code=404, detail="Item not found")
return {"item": items[item_id]}

The resulting response

If the client requests http://example.com/items/foo (an item_id "foo" ), that client will receive an
HTTP status code of 200, and a JSON response of:

{
"item": "The Foo Wrestlers"
}

But if the client requests http://example.com/items/bar (a non-existent item_id "bar" ), that


client will receive an HTTP status code of 404 (the "not found" error), and a JSON response of:

{
"detail": "Item not found"
}

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Tip

When raising an HTTPException , you can pass any value that can be converted to JSON as the parameter detail , not only
str .

You could pass a dict , a list , etc.

They are handled automatically by FastAPI and converted to JSON.

Add custom headers


There are some situations in where it's useful to be able to add custom headers to the HTTP error. For
example, for some types of security.

You probably won't need to use it directly in your code.

But in case you needed it for an advanced scenario, you can add custom headers:

from fastapi import FastAPI, HTTPException

app = FastAPI()

items = {"foo": "The Foo Wrestlers"}

@app.get("/items-header/{item_id}")
async def read_item_header(item_id: str):
if item_id not in items:
raise HTTPException(
status_code=404,
detail="Item not found",
headers={"X-Error": "There goes my error"},
)
return {"item": items[item_id]}

Install custom exception handlers


You can add custom exception handlers with the same exception utilities from Starlette [↪].

Let's say you have a custom exception UnicornException that you (or a library you use) might raise .

And you want to handle this exception globally with FastAPI.

You could add a custom exception handler with @app.exception_handler() :

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse

class UnicornException(Exception):
def __init__(self, name: str):
self.name = name

app = FastAPI()

@app.exception_handler(UnicornException)
async def unicorn_exception_handler(request: Request, exc: UnicornException):
return JSONResponse(
status_code=418,
content={"message": f"Oops! {exc.name} did something. There goes a rainbow..."},
)

@app.get("/unicorns/{name}")
async def read_unicorn(name: str):
if name == "yolo":
raise UnicornException(name=name)
return {"unicorn_name": name}

Here, if you request /unicorns/yolo , the path operation will raise a UnicornException .

But it will be handled by the unicorn_exception_handler .

So, you will receive a clean error, with an HTTP status code of 418 and a JSON content of:

{"message": "Oops! yolo did something. There goes a rainbow..."}

Technical Details

You could also use from starlette.requests import Request and from starlette.responses import JSONResponse .

FastAPI provides the same starlette.responses as fastapi.responses just as a convenience for you, the developer. But
most of the available responses come directly from Starlette. The same with Request .

Override the default exception handlers


FastAPI has some default exception handlers.

These handlers are in charge of returning the default JSON responses when you raise an
HTTPException and when the request has invalid data.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
You can override these exception handlers with your own.

Override request validation exceptions

When a request contains invalid data, FastAPI internally raises a RequestValidationError .

And it also includes a default exception handler for it.

To override it, import the RequestValidationError and use it with


@app.exception_handler(RequestValidationError) to decorate the exception handler.

The exception handler will receive a Request and the exception.

from fastapi import FastAPI, HTTPException


from fastapi.exceptions import RequestValidationError
from fastapi.responses import PlainTextResponse
from starlette.exceptions import HTTPException as StarletteHTTPException

app = FastAPI()

@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request, exc):
return PlainTextResponse(str(exc.detail), status_code=exc.status_code)

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
return PlainTextResponse(str(exc), status_code=400)

@app.get("/items/{item_id}")
async def read_item(item_id: int):
if item_id == 3:
raise HTTPException(status_code=418, detail="Nope! I don't like 3.")
return {"item_id": item_id}

Now, if you go to /items/foo , instead of getting the default JSON error with:

{
"detail": [
{
"loc": [
"path",
"item_id"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
]
}

you will get a text version, with:

1 validation error
path -> item_id
value is not a valid integer (type=type_error.integer)

RequestValidationError vs ValidationError

Warning

These are technical details that you might skip if it's not important for you now.

RequestValidationError is a sub-class of Pydantic's ValidationError [↪].

FastAPI uses it so that, if you use a Pydantic model in response_model , and your data has an error,
you will see the error in your log.

But the client/user will not see it. Instead, the client will receive an "Internal Server Error" with a HTTP
status code 500 .

It should be this way because if you have a Pydantic ValidationError in your response or anywhere
in your code (not in the client's request), it's actually a bug in your code.

And while you fix it, your clients/users shouldn't have access to internal information about the error, as
that could expose a security vulnerability.

Override the HTTPException error handler

The same way, you can override the HTTPException handler.

For example, you could want to return a plain text response instead of JSON for these errors:

from fastapi import FastAPI, HTTPException


from fastapi.exceptions import RequestValidationError
from fastapi.responses import PlainTextResponse
from starlette.exceptions import HTTPException as StarletteHTTPException

app = FastAPI()

@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request, exc):
return PlainTextResponse(str(exc.detail), status_code=exc.status_code)

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
return PlainTextResponse(str(exc), status_code=400)

@app.get("/items/{item_id}")
async def read_item(item_id: int):
if item_id == 3:
raise HTTPException(status_code=418, detail="Nope! I don't like 3.")
return {"item_id": item_id}

Technical Details

You could also use from starlette.responses import PlainTextResponse .

FastAPI provides the same starlette.responses as fastapi.responses just as a convenience for you, the developer. But
most of the available responses come directly from Starlette.

Use the RequestValidationError body

The RequestValidationError contains the body it received with invalid data.

You could use it while developing your app to log the body and debug it, return it to the user, etc.

from fastapi import FastAPI, Request, status


from fastapi.encoders import jsonable_encoder
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from pydantic import BaseModel

app = FastAPI()

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
return JSONResponse(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
content=jsonable_encoder({"detail": exc.errors(), "body": exc.body}),
)

class Item(BaseModel):
title: str
size: int

@app.post("/items/")

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
async def create_item(item: Item):
return item

Now try sending an invalid item like:

{
"title": "towel",
"size": "XL"
}

You will receive a response telling you that the data is invalid containing the received body:

{
"detail": [
{
"loc": [
"body",
"size"
],
"msg": "value is not a valid integer",
"type": "type_error.integer"
}
],
"body": {
"title": "towel",
"size": "XL"
}
}

FastAPI's HTTPException vs Starlette's HTTPException

FastAPI has its own HTTPException .

And FastAPI's HTTPException error class inherits from Starlette's HTTPException error class.

The only difference is that FastAPI's HTTPException accepts any JSON-able data for the detail field,
while Starlette's HTTPException only accepts strings for it.

So, you can keep raising FastAPI's HTTPException as normally in your code.

But when you register an exception handler, you should register it for Starlette's HTTPException .

This way, if any part of Starlette's internal code, or a Starlette extension or plug-in, raises a Starlette
HTTPException , your handler will be able to catch and handle it.

In this example, to be able to have both HTTPException s in the same code, Starlette's exceptions is
renamed to StarletteHTTPException :

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
from starlette.exceptions import HTTPException as StarletteHTTPException

Reuse FastAPI's exception handlers

If you want to use the exception along with the same default exception handlers from FastAPI, You
can import and reuse the default exception handlers from fastapi.exception_handlers :

from fastapi import FastAPI, HTTPException


from fastapi.exception_handlers import (
http_exception_handler,
request_validation_exception_handler,
)
from fastapi.exceptions import RequestValidationError
from starlette.exceptions import HTTPException as StarletteHTTPException

app = FastAPI()

@app.exception_handler(StarletteHTTPException)
async def custom_http_exception_handler(request, exc):
print(f"OMG! An HTTP error!: {repr(exc)}")
return await http_exception_handler(request, exc)

@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
print(f"OMG! The client sent invalid data!: {exc}")
return await request_validation_exception_handler(request, exc)

@app.get("/items/{item_id}")
async def read_item(item_id: int):
if item_id == 3:
raise HTTPException(status_code=418, detail="Nope! I don't like 3.")
return {"item_id": item_id}

In this example you are just print ing the error with a very expressive message, but you get the idea.
You can use the exception and then just reuse the default exception handlers.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Path Operation Configuration

There are several parameters that you can pass to your path operation decorator to configure it.

Warning

Notice that these parameters are passed directly to the path operation decorator, not to your path operation function.

Response Status Code


You can define the (HTTP) status_code to be used in the response of your path operation.

You can pass directly the int code, like 404 .

But if you don't remember what each number code is for, you can use the shortcut constants in status :

Python 3.10+ Python 3.9+ Python 3.8+

from fastapi import FastAPI, status


from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()

@app.post("/items/", response_model=Item, status_code=status.HTTP_201_CREATED)


async def create_item(item: Item):
return item

That status code will be used in the response and will be added to the OpenAPI schema.

Technical Details

You could also use from starlette import status .

FastAPI provides the same starlette.status as fastapi.status just as a convenience for you, the developer. But it comes directly from Starlette.

Tags
You can add tags to your path operation, pass the parameter tags with a list of str (commonly just one str ):

Python 3.10+ Python 3.9+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
name: str
description: str | None = None
price: float

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
tax: float | None = None
tags: set[str] = set()

@app.post("/items/", response_model=Item, tags=["items"])


async def create_item(item: Item):
return item

@app.get("/items/", tags=["items"])
async def read_items():
return [{"name": "Foo", "price": 42}]

@app.get("/users/", tags=["users"])
async def read_users():
return [{"username": "johndoe"}]

They will be added to the OpenAPI schema and used by the automatic documentation interfaces:

Tags with Enums

If you have a big application, you might end up accumulating several tags, and you would want to make sure you always use the same
tag for related path operations.

In these cases, it could make sense to store the tags in an Enum .

FastAPI supports that the same way as with plain strings:

from enum import Enum

from fastapi import FastAPI

app = FastAPI()

class Tags(Enum):
items = "items"
users = "users"

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
@app.get("/items/", tags=[Tags.items])
async def get_items():
return ["Portal gun", "Plumbus"]

@app.get("/users/", tags=[Tags.users])
async def read_users():
return ["Rick", "Morty"]

Summary and description


You can add a summary and description :

Python 3.10+ Python 3.9+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()

@app.post(
"/items/",
response_model=Item,
summary="Create an item",
description="Create an item with all the information, name, description, price, tax and a set of unique tags",
)
async def create_item(item: Item):
return item

Description from docstring


As descriptions tend to be long and cover multiple lines, you can declare the path operation description in the function docstring and
FastAPI will read it from there.

You can write Markdown [↪] in the docstring, it will be interpreted and displayed correctly (taking into account docstring indentation).

Python 3.10+ Python 3.9+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()

@app.post("/items/", response_model=Item, summary="Create an item")


async def create_item(item: Item):

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
"""
Create an item with all the information:

- **name**: each item must have a name


- **description**: a long description
- **price**: required
- **tax**: if the item doesn't have tax, you can omit this
- **tags**: a set of unique tag strings for this item
"""
return item

It will be used in the interactive docs:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Response description
You can specify the response description with the parameter response_description :

Python 3.10+ Python 3.9+ Python 3.8+

from fastapi import FastAPI


from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
tags: set[str] = set()

@app.post(
"/items/",
response_model=Item,
summary="Create an item",
response_description="The created item",
)
async def create_item(item: Item):
"""
Create an item with all the information:

- **name**: each item must have a name


- **description**: a long description
- **price**: required
- **tax**: if the item doesn't have tax, you can omit this
- **tags**: a set of unique tag strings for this item
"""
return item

Info

Notice that response_description refers specifically to the response, the description refers to the path operation in general.

Check

OpenAPI specifies that each path operation requires a response description.

So, if you don't provide one, FastAPI will automatically generate one of "Successful response".

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Deprecate a path operation
If you need to mark a path operation as deprecated, but without removing it, pass the parameter deprecated :

from fastapi import FastAPI

app = FastAPI()

@app.get("/items/", tags=["items"])
async def read_items():
return [{"name": "Foo", "price": 42}]

@app.get("/users/", tags=["users"])
async def read_users():
return [{"username": "johndoe"}]

@app.get("/elements/", tags=["items"], deprecated=True)


async def read_elements():
return [{"item_id": "Foo"}]

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
It will be clearly marked as deprecated in the interactive docs:

Check how deprecated and non-deprecated path operations look like:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Recap
You can configure and add metadata for your path operations easily by passing parameters to the path operation decorators.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
JSON Compatible Encoder

There are some cases where you might need to convert a data type (like a Pydantic model) to
something compatible with JSON (like a dict , list , etc).

For example, if you need to store it in a database.

For that, FastAPI provides a jsonable_encoder() function.

Using the jsonable_encoder


Let's imagine that you have a database fake_db that only receives JSON compatible data.

For example, it doesn't receive datetime objects, as those are not compatible with JSON.

So, a datetime object would have to be converted to a str containing the data in ISO format [↪].

The same way, this database wouldn't receive a Pydantic model (an object with attributes), only a
dict .

You can use jsonable_encoder for that.

It receives an object, like a Pydantic model, and returns a JSON compatible version:

Python 3.10+ Python 3.8+

from datetime import datetime

from fastapi import FastAPI


from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel

fake_db = {}

class Item(BaseModel):
title: str
timestamp: datetime
description: str | None = None

app = FastAPI()

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
@app.put("/items/{id}")
def update_item(id: str, item: Item):
json_compatible_item_data = jsonable_encoder(item)
fake_db[id] = json_compatible_item_data

In this example, it would convert the Pydantic model to a dict , and the datetime to a str .

The result of calling it is something that can be encoded with the Python standard
json.dumps() [↪].

It doesn't return a large str containing the data in JSON format (as a string). It returns a Python
standard data structure (e.g. a dict ) with values and sub-values that are all compatible with JSON.

Note

jsonable_encoder is actually used by FastAPI internally to convert data. But it is useful in many other scenarios.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Body - Updates

Update replacing with PUT


To update an item you can use the HTTP PUT [↪] operation.

You can use the jsonable_encoder to convert the input data to data that can be stored as JSON (e.g. with
a NoSQL database). For example, converting datetime to str .

Python 3.10+ Python 3.9+ Python 3.8+

from fastapi import FastAPI


from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
name: str | None = None
description: str | None = None
price: float | None = None
tax: float = 10.5
tags: list[str] = []

items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}

@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: str):
return items[item_id]

@app.put("/items/{item_id}", response_model=Item)
async def update_item(item_id: str, item: Item):
update_item_encoded = jsonable_encoder(item)
items[item_id] = update_item_encoded
return update_item_encoded

PUT is used to receive data that should replace the existing data.

Warning about replacing

That means that if you want to update the item bar using PUT with a body containing:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
{
"name": "Barz",
"price": 3,
"description": None,
}

because it doesn't include the already stored attribute "tax": 20.2 , the input model would take the
default value of "tax": 10.5 .

And the data would be saved with that "new" tax of 10.5 .

Partial updates with PATCH


You can also use the HTTP PATCH [↪] operation to partially update data.

This means that you can send only the data that you want to update, leaving the rest intact.

Note

PATCH is less commonly used and known than PUT .

And many teams use only PUT , even for partial updates.

You are free to use them however you want, FastAPI doesn't impose any restrictions.

But this guide shows you, more or less, how they are intended to be used.

Using Pydantic's exclude_unset parameter

If you want to receive partial updates, it's very useful to use the parameter exclude_unset in Pydantic's
model's .model_dump() .

Like item.model_dump(exclude_unset=True) .

Info

In Pydantic v1 the method was called .dict() , it was deprecated (but still supported) in Pydantic v2, and renamed to
.model_dump() .

The examples here use .dict() for compatibility with Pydantic v1, but you should use .model_dump() instead if you can use
Pydantic v2.

That would generate a dict with only the data that was set when creating the item model, excluding
default values.

Then you can use this to generate a dict with only the data that was set (sent in the request), omitting
default values:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Python 3.10+ Python 3.9+ Python 3.8+

from fastapi import FastAPI


from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
name: str | None = None
description: str | None = None
price: float | None = None
tax: float = 10.5
tags: list[str] = []

items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}

@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: str):
return items[item_id]

@app.patch("/items/{item_id}", response_model=Item)
async def update_item(item_id: str, item: Item):
stored_item_data = items[item_id]
stored_item_model = Item(**stored_item_data)
update_data = item.dict(exclude_unset=True)
updated_item = stored_item_model.copy(update=update_data)
items[item_id] = jsonable_encoder(updated_item)
return updated_item

Using Pydantic's update parameter

Now, you can create a copy of the existing model using .model_copy() , and pass the update parameter
with a dict containing the data to update.

Info

In Pydantic v1 the method was called .copy() , it was deprecated (but still supported) in Pydantic v2, and renamed to
.model_copy() .

The examples here use .copy() for compatibility with Pydantic v1, but you should use .model_copy() instead if you can use
Pydantic v2.

Like stored_item_model.model_copy(update=update_data) :

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Python 3.10+ Python 3.9+ Python 3.8+

from fastapi import FastAPI


from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
name: str | None = None
description: str | None = None
price: float | None = None
tax: float = 10.5
tags: list[str] = []

items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}

@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: str):
return items[item_id]

@app.patch("/items/{item_id}", response_model=Item)
async def update_item(item_id: str, item: Item):
stored_item_data = items[item_id]
stored_item_model = Item(**stored_item_data)
update_data = item.dict(exclude_unset=True)
updated_item = stored_item_model.copy(update=update_data)
items[item_id] = jsonable_encoder(updated_item)
return updated_item

Partial updates recap

In summary, to apply partial updates you would:

(Optionally) use PATCH instead of PUT .

Retrieve the stored data.

Put that data in a Pydantic model.

Generate a dict without default values from the input model (using exclude_unset ).

This way you can update only the values actually set by the user, instead of overriding values
already stored with default values in your model.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Create a copy of the stored model, updating it's attributes with the received partial updates (using the
update parameter).

Convert the copied model to something that can be stored in your DB (for example, using the
jsonable_encoder ).

This is comparable to using the model's .model_dump() method again, but it makes sure (and
converts) the values to data types that can be converted to JSON, for example, datetime to str .

Save the data to your DB.

Return the updated model.

Python 3.10+ Python 3.9+ Python 3.8+

from fastapi import FastAPI


from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
name: str | None = None
description: str | None = None
price: float | None = None
tax: float = 10.5
tags: list[str] = []

items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}

@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: str):
return items[item_id]

@app.patch("/items/{item_id}", response_model=Item)
async def update_item(item_id: str, item: Item):
stored_item_data = items[item_id]
stored_item_model = Item(**stored_item_data)
update_data = item.dict(exclude_unset=True)
updated_item = stored_item_model.copy(update=update_data)
items[item_id] = jsonable_encoder(updated_item)
return updated_item

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Tip

You can actually use this same technique with an HTTP PUT operation.

But the example here uses PATCH because it was created for these use cases.

Note

Notice that the input model is still validated.

So, if you want to receive partial updates that can omit all the attributes, you need to have a model with all the attributes marked
as optional (with default values or None ).

To distinguish from the models with all optional values for updates and models with required values for creation, you can use the
ideas described in Extra Models ↪.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Dependencies

FastAPI has a very powerful but intuitive Dependency Injection system.

It is designed to be very simple to use, and to make it very easy for any developer to integrate other
components with FastAPI.

What is "Dependency Injection"


"Dependency Injection" means, in programming, that there is a way for your code (in this case, your
path operation functions) to declare things that it requires to work and use: "dependencies".

And then, that system (in this case FastAPI) will take care of doing whatever is needed to provide
your code with those needed dependencies ("inject" the dependencies).

This is very useful when you need to:

Have shared logic (the same code logic again and again).

Share database connections.

Enforce security, authentication, role requirements, etc.

And many other things...

All these, while minimizing code repetition.

First Steps
Let's see a very simple example. It will be so simple that it is not very useful, for now.

But this way we can focus on how the Dependency Injection system works.

Create a dependency, or "dependable"

Let's first focus on the dependency.

It is just a function that can take all the same parameters that a path operation function can take:

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
from typing import Annotated

from fastapi import Depends, FastAPI

app = FastAPI()

async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}

@app.get("/items/")
async def read_items(commons: Annotated[dict, Depends(common_parameters)]):
return commons

@app.get("/users/")
async def read_users(commons: Annotated[dict, Depends(common_parameters)]):
return commons

That's it.

2 lines.

And it has the same shape and structure that all your path operation functions have.

You can think of it as a path operation function without the "decorator" (without the
@app.get("/some-path") ).

And it can return anything you want.

In this case, this dependency expects:

An optional query parameter q that is a str .

An optional query parameter skip that is an int , and by default is 0 .

An optional query parameter limit that is an int , and by default is 100 .

And then it just returns a dict containing those values.

Info

FastAPI added support for Annotated (and started recommending it) in version 0.95.0.

If you have an older version, you would get errors when trying to use Annotated .

Make sure you Upgrade the FastAPI version ↪ to at least 0.95.1 before using Annotated .

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Import Depends

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI

app = FastAPI()

async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}

@app.get("/items/")
async def read_items(commons: Annotated[dict, Depends(common_parameters)]):
return commons

@app.get("/users/")
async def read_users(commons: Annotated[dict, Depends(common_parameters)]):
return commons

Declare the dependency, in the "dependant"

The same way you use Body , Query , etc. with your path operation function parameters, use
Depends with a new parameter:

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI

app = FastAPI()

async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}

@app.get("/items/")
async def read_items(commons: Annotated[dict, Depends(common_parameters)]):
return commons

@app.get("/users/")
async def read_users(commons: Annotated[dict, Depends(common_parameters)]):

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
return commons

Although you use Depends in the parameters of your function the same way you use Body , Query ,
etc, Depends works a bit differently.

You only give Depends a single parameter.

This parameter must be something like a function.

You don't call it directly (don't add the parenthesis at the end), you just pass it as a parameter to
Depends() .

And that function takes parameters in the same way that path operation functions do.

Tip

You'll see what other "things", apart from functions, can be used as dependencies in the next chapter.

Whenever a new request arrives, FastAPI will take care of:

Calling your dependency ("dependable") function with the correct parameters.

Get the result from your function.

Assign that result to the parameter in your path operation function.

This way you write shared code once and FastAPI takes care of calling it for your path operations.

Check

Notice that you don't have to create a special class and pass it somewhere to FastAPI to "register" it or anything similar.

You just pass it to Depends and FastAPI knows how to do the rest.

Share Annotated dependencies


In the examples above, you see that there's a tiny bit of code duplication.

When you need to use the common_parameters() dependency, you have to write the whole parameter
with the type annotation and Depends() :

commons: Annotated[dict, Depends(common_parameters)]

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
But because we are using Annotated , we can store that Annotated value in a variable and use it in
multiple places:

Python 3.10+ Python 3.9+ Python 3.8+

from typing import Annotated

from fastapi import Depends, FastAPI

app = FastAPI()

async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}

CommonsDep = Annotated[dict, Depends(common_parameters)]

@app.get("/items/")
async def read_items(commons: CommonsDep):
return commons

@app.get("/users/")
async def read_users(commons: CommonsDep):
return commons

Tip

This is just standard Python, it's called a "type alias", it's actually not specific to FastAPI.

But because FastAPI is based on the Python standards, including Annotated , you can use this trick in your code. 😎
The dependencies will keep working as expected, and the best part is that the type information will
be preserved, which means that your editor will be able to keep providing you with autocompletion,
inline errors, etc. The same for other tools like mypy .

This will be especially useful when you use it in a large code base where you use the same
dependencies over and over again in many path operations.

To async or not to async


As dependencies will also be called by FastAPI (the same as your path operation functions), the same
rules apply while defining your functions.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
You can use async def or normal def .

And you can declare dependencies with async def inside of normal def path operation functions,
or def dependencies inside of async def path operation functions, etc.

It doesn't matter. FastAPI will know what to do.

Note

If you don't know, check the Async: "In a hurry?" ↪ section about async and await in the docs.

Integrated with OpenAPI


All the request declarations, validations and requirements of your dependencies (and sub-
dependencies) will be integrated in the same OpenAPI schema.

So, the interactive docs will have all the information from these dependencies too:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Simple usage
If you look at it, path operation functions are declared to be used whenever a path and operation
matches, and then FastAPI takes care of calling the function with the correct parameters, extracting
the data from the request.

Actually, all (or most) of the web frameworks work in this same way.

You never call those functions directly. They are called by your framework (in this case, FastAPI).

With the Dependency Injection system, you can also tell FastAPI that your path operation function
also "depends" on something else that should be executed before your path operation function, and
FastAPI will take care of executing it and "injecting" the results.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Other common terms for this same idea of "dependency injection" are:

resources

providers

services

injectables

components

FastAPI plug-ins
Integrations and "plug-ins" can be built using the Dependency Injection system. But in fact, there is
actually no need to create "plug-ins", as by using dependencies it's possible to declare an infinite
number of integrations and interactions that become available to your path operation functions.

And dependencies can be created in a very simple and intuitive way that allows you to just import the
Python packages you need, and integrate them with your API functions in a couple of lines of code,
literally.

You will see examples of this in the next chapters, about relational and NoSQL databases, security,
etc.

FastAPI compatibility
The simplicity of the dependency injection system makes FastAPI compatible with:

all the relational databases

NoSQL databases

external packages

external APIs

authentication and authorization systems

API usage monitoring systems

response data injection systems

etc.

Simple and Powerful

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Although the hierarchical dependency injection system is very simple to define and use, it's still very
powerful.

You can define dependencies that in turn can define dependencies themselves.

In the end, a hierarchical tree of dependencies is built, and the Dependency Injection system takes
care of solving all these dependencies for you (and their sub-dependencies) and providing (injecting)
the results at each step.

For example, let's say you have 4 API endpoints (path operations):

/items/public/

/items/private/

/users/{user_id}/activate

/items/pro/

then you could add different permission requirements for each of them just with dependencies and
sub-dependencies:

Integrated with OpenAPI


All these dependencies, while declaring their requirements, also add parameters, validations, etc. to
your path operations.

FastAPI will take care of adding it all to the OpenAPI schema, so that it is shown in the interactive
documentation systems.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Classes as Dependencies

Before diving deeper into the Dependency Injection system, let's upgrade the previous example.

A dict from the previous example


In the previous example, we were returning a dict from our dependency ("dependable"):

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI

app = FastAPI()

async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}

@app.get("/items/")
async def read_items(commons: Annotated[dict, Depends(common_parameters)]):
return commons

@app.get("/users/")
async def read_users(commons: Annotated[dict, Depends(common_parameters)]):
return commons

But then we get a dict in the parameter commons of the path operation function.

And we know that editors can't provide a lot of support (like completion) for dict s, because they can't
know their keys and value types.

We can do better...

What makes a dependency


Up to now you have seen dependencies declared as functions.

But that's not the only way to declare dependencies (although it would probably be the more common).

The key factor is that a dependency should be a "callable".

A "callable" in Python is anything that Python can "call" like a function.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
So, if you have an object something (that might not be a function) and you can "call" it (execute it) like:

something()

or

something(some_argument, some_keyword_argument="foo")

then it is a "callable".

Classes as dependencies
You might notice that to create an instance of a Python class, you use that same syntax.

For example:

class Cat:
def __init__(self, name: str):
self.name = name

fluffy = Cat(name="Mr Fluffy")

In this case, fluffy is an instance of the class Cat .

And to create fluffy , you are "calling" Cat .

So, a Python class is also a callable.

Then, in FastAPI, you could use a Python class as a dependency.

What FastAPI actually checks is that it is a "callable" (function, class or anything else) and the parameters
defined.

If you pass a "callable" as a dependency in FastAPI, it will analyze the parameters for that "callable", and
process them in the same way as the parameters for a path operation function. Including sub-
dependencies.

That also applies to callables with no parameters at all. The same as it would be for path operation
functions with no parameters.

Then, we can change the dependency "dependable" common_parameters from above to the class
CommonQueryParams :

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
from typing import Annotated

from fastapi import Depends, FastAPI

app = FastAPI()

fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]

class CommonQueryParams:
def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit

@app.get("/items/")
async def read_items(commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]
response.update({"items": items})
return response

Pay attention to the __init__ method used to create the instance of the class:

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI

app = FastAPI()

fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]

class CommonQueryParams:
def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit

@app.get("/items/")
async def read_items(commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
response.update({"items": items})
return response

...it has the same parameters as our previous common_parameters :

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI

app = FastAPI()

async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
return {"q": q, "skip": skip, "limit": limit}

@app.get("/items/")
async def read_items(commons: Annotated[dict, Depends(common_parameters)]):
return commons

@app.get("/users/")
async def read_users(commons: Annotated[dict, Depends(common_parameters)]):
return commons

Those parameters are what FastAPI will use to "solve" the dependency.

In both cases, it will have:

An optional q query parameter that is a str .

A skip query parameter that is an int , with a default of 0 .

A limit query parameter that is an int , with a default of 100 .

In both cases the data will be converted, validated, documented on the OpenAPI schema, etc.

Use it
Now you can declare your dependency using this class.

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI

app = FastAPI()

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]

class CommonQueryParams:
def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit

@app.get("/items/")
async def read_items(commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]
response.update({"items": items})
return response

FastAPI calls the CommonQueryParams class. This creates an "instance" of that class and the instance will
be passed as the parameter commons to your function.

Type annotation vs Depends


Notice how we write CommonQueryParams twice in the above code:

Python 3.8+ Python 3.8+ non-Annotated

commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]

The last CommonQueryParams , in:

... Depends(CommonQueryParams)

...is what FastAPI will actually use to know what is the dependency.

From it is that FastAPI will extract the declared parameters and that is what FastAPI will actually call.

In this case, the first CommonQueryParams , in:

Python 3.8+ Python 3.8+ non-Annotated

commons: Annotated[CommonQueryParams, ...

...doesn't have any special meaning for FastAPI. FastAPI won't use it for data conversion, validation, etc.
(as it is using the Depends(CommonQueryParams) for that).

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
You could actually write just:

Python 3.8+ Python 3.8+ non-Annotated

commons: Annotated[Any, Depends(CommonQueryParams)]

...as in:

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated, Any

from fastapi import Depends, FastAPI

app = FastAPI()

fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]

class CommonQueryParams:
def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit

@app.get("/items/")
async def read_items(commons: Annotated[Any, Depends(CommonQueryParams)]):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]
response.update({"items": items})
return response

But declaring the type is encouraged as that way your editor will know what will be passed as the
parameter commons , and then it can help you with code completion, type checks, etc:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Shortcut
But you see that we are having some code repetition here, writing CommonQueryParams twice:

Python 3.8+ Python 3.8+ non-Annotated

commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]

FastAPI provides a shortcut for these cases, in where the dependency is specifically a class that FastAPI
will "call" to create an instance of the class itself.

For those specific cases, you can do the following:

Instead of writing:

Python 3.8+ Python 3.8+ non-Annotated

commons: Annotated[CommonQueryParams, Depends(CommonQueryParams)]

...you write:

Python 3.8+ Python 3.8 non-Annotated

commons: Annotated[CommonQueryParams, Depends()]

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
You declare the dependency as the type of the parameter, and you use Depends() without any parameter,
instead of having to write the full class again inside of Depends(CommonQueryParams) .

The same example would then look like:

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI

app = FastAPI()

fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]

class CommonQueryParams:
def __init__(self, q: str | None = None, skip: int = 0, limit: int = 100):
self.q = q
self.skip = skip
self.limit = limit

@app.get("/items/")
async def read_items(commons: Annotated[CommonQueryParams, Depends()]):
response = {}
if commons.q:
response.update({"q": commons.q})
items = fake_items_db[commons.skip : commons.skip + commons.limit]
response.update({"items": items})
return response

...and FastAPI will know what to do.

Tip

If that seems more confusing than helpful, disregard it, you don't need it.

It is just a shortcut. Because FastAPI cares about helping you minimize code repetition.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Sub-dependencies

You can create dependencies that have sub-dependencies.

They can be as deep as you need them to be.

FastAPI will take care of solving them.

First dependency "dependable"


You could create a first dependency ("dependable") like:

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10 non-Annotated Python 3.8 non-Annotated

from typing import Annotated

from fastapi import Cookie, Depends, FastAPI

app = FastAPI()

def query_extractor(q: str | None = None):


return q

def query_or_cookie_extractor(
q: Annotated[str, Depends(query_extractor)],
last_query: Annotated[str | None, Cookie()] = None,
):
if not q:
return last_query
return q

@app.get("/items/")
async def read_query(
query_or_default: Annotated[str, Depends(query_or_cookie_extractor)],
):
return {"q_or_cookie": query_or_default}

It declares an optional query parameter q as a str , and then it just returns it.

This is quite simple (not very useful), but will help us focus on how the sub-dependencies work.

Second dependency, "dependable" and "dependant"


Then you can create another dependency function (a "dependable") that at the same time declares a dependency
of its own (so it is a "dependant" too):

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10 non-Annotated Python 3.8 non-Annotated

from typing import Annotated

from fastapi import Cookie, Depends, FastAPI

app = FastAPI()

def query_extractor(q: str | None = None):


return q

def query_or_cookie_extractor(
q: Annotated[str, Depends(query_extractor)],
last_query: Annotated[str | None, Cookie()] = None,
):
if not q:
return last_query
return q

@app.get("/items/")
async def read_query(
query_or_default: Annotated[str, Depends(query_or_cookie_extractor)],
):
return {"q_or_cookie": query_or_default}

Let's focus on the parameters declared:

Even though this function is a dependency ("dependable") itself, it also declares another dependency (it
"depends" on something else).

It depends on the query_extractor , and assigns the value returned by it to the parameter q .

It also declares an optional last_query cookie, as a str .

If the user didn't provide any query q , we use the last query used, which we saved to a cookie before.

Use the dependency


Then we can use the dependency with:

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10 non-Annotated Python 3.8 non-Annotated

from typing import Annotated

from fastapi import Cookie, Depends, FastAPI

app = FastAPI()

def query_extractor(q: str | None = None):


return q

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
def query_or_cookie_extractor(
q: Annotated[str, Depends(query_extractor)],
last_query: Annotated[str | None, Cookie()] = None,
):
if not q:
return last_query
return q

@app.get("/items/")
async def read_query(
query_or_default: Annotated[str, Depends(query_or_cookie_extractor)],
):
return {"q_or_cookie": query_or_default}

Info

Notice that we are only declaring one dependency in the path operation function, the query_or_cookie_extractor .

But FastAPI will know that it has to solve query_extractor first, to pass the results of that to query_or_cookie_extractor while
calling it.

Using the same dependency multiple times


If one of your dependencies is declared multiple times for the same path operation, for example, multiple
dependencies have a common sub-dependency, FastAPI will know to call that sub-dependency only once per
request.

And it will save the returned value in a "cache" and pass it to all the "dependants" that need it in that specific
request, instead of calling the dependency multiple times for the same request.

In an advanced scenario where you know you need the dependency to be called at every step (possibly multiple
times) in the same request instead of using the "cached" value, you can set the parameter use_cache=False
when using Depends :

Python 3.8+ Python 3.8+ non-Annotated

async def needy_dependency(fresh_value: Annotated[str, Depends(get_value, use_cache=False)]):


return {"fresh_value": fresh_value}

Recap
Apart from all the fancy words used here, the Dependency Injection system is quite simple.

Just functions that look the same as the path operation functions.

But still, it is very powerful, and allows you to declare arbitrarily deeply nested dependency "graphs" (trees).

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Tip

All this might not seem as useful with these simple examples.

But you will see how useful it is in the chapters about security.

And you will also see the amounts of code it will save you.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Dependencies in path operation decorators

In some cases you don't really need the return value of a dependency inside your path operation
function.

Or the dependency doesn't return a value.

But you still need it to be executed/solved.

For those cases, instead of declaring a path operation function parameter with Depends , you can
add a list of dependencies to the path operation decorator.

Add dependencies to the path operation decorator


The path operation decorator receives an optional argument dependencies .

It should be a list of Depends() :

Python 3.9+ Python 3.8+ Python 3.8 non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI, Header, HTTPException

app = FastAPI()

async def verify_token(x_token: Annotated[str, Header()]):


if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")

async def verify_key(x_key: Annotated[str, Header()]):


if x_key != "fake-super-secret-key":
raise HTTPException(status_code=400, detail="X-Key header invalid")
return x_key

@app.get("/items/", dependencies=[Depends(verify_token), Depends(verify_key)])


async def read_items():
return [{"item": "Foo"}, {"item": "Bar"}]

These dependencies will be executed/solved the same way as normal dependencies. But their value
(if they return any) won't be passed to your path operation function.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Tip

Some editors check for unused function parameters, and show them as errors.

Using these dependencies in the path operation decorator you can make sure they are executed while avoiding
editor/tooling errors.

It might also help avoid confusion for new developers that see an unused parameter in your code and could think it's
unnecessary.

Info

In this example we use invented custom headers X-Key and X-Token .

But in real cases, when implementing security, you would get more benefits from using the integrated Security utilities
(the next chapter) ↪.

Dependencies errors and return values


You can use the same dependency functions you use normally.

Dependency requirements

They can declare request requirements (like headers) or other sub-dependencies:

Python 3.9+ Python 3.8+ Python 3.8 non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI, Header, HTTPException

app = FastAPI()

async def verify_token(x_token: Annotated[str, Header()]):


if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")

async def verify_key(x_key: Annotated[str, Header()]):


if x_key != "fake-super-secret-key":
raise HTTPException(status_code=400, detail="X-Key header invalid")
return x_key

@app.get("/items/", dependencies=[Depends(verify_token), Depends(verify_key)])

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
async def read_items():
return [{"item": "Foo"}, {"item": "Bar"}]

Raise exceptions

These dependencies can raise exceptions, the same as normal dependencies:

Python 3.9+ Python 3.8+ Python 3.8 non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI, Header, HTTPException

app = FastAPI()

async def verify_token(x_token: Annotated[str, Header()]):


if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")

async def verify_key(x_key: Annotated[str, Header()]):


if x_key != "fake-super-secret-key":
raise HTTPException(status_code=400, detail="X-Key header invalid")
return x_key

@app.get("/items/", dependencies=[Depends(verify_token), Depends(verify_key)])


async def read_items():
return [{"item": "Foo"}, {"item": "Bar"}]

Return values

And they can return values or not, the values won't be used.

So, you can reuse a normal dependency (that returns a value) you already use somewhere else, and
even though the value won't be used, the dependency will be executed:

Python 3.9+ Python 3.8+ Python 3.8 non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI, Header, HTTPException

app = FastAPI()

async def verify_token(x_token: Annotated[str, Header()]):

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")

async def verify_key(x_key: Annotated[str, Header()]):


if x_key != "fake-super-secret-key":
raise HTTPException(status_code=400, detail="X-Key header invalid")
return x_key

@app.get("/items/", dependencies=[Depends(verify_token), Depends(verify_key)])


async def read_items():
return [{"item": "Foo"}, {"item": "Bar"}]

Dependencies for a group of path operations


Later, when reading about how to structure bigger applications (Bigger Applications - Multiple
Files ↪), possibly with multiple files, you will learn how to declare a single dependencies parameter
for a group of path operations.

Global Dependencies
Next we will see how to add dependencies to the whole FastAPI application, so that they apply to
each path operation.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Global Dependencies

For some types of applications you might want to add dependencies to the whole application.

Similar to the way you can add dependencies to the path operation decorators ↪, you can add them
to the FastAPI application.

In that case, they will be applied to all the path operations in the application:

Python 3.9+ Python 3.8+ Python 3.8 non-Annotated

from fastapi import Depends, FastAPI, Header, HTTPException


from typing_extensions import Annotated

async def verify_token(x_token: Annotated[str, Header()]):


if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")

async def verify_key(x_key: Annotated[str, Header()]):


if x_key != "fake-super-secret-key":
raise HTTPException(status_code=400, detail="X-Key header invalid")
return x_key

app = FastAPI(dependencies=[Depends(verify_token), Depends(verify_key)])

@app.get("/items/")
async def read_items():
return [{"item": "Portal Gun"}, {"item": "Plumbus"}]

@app.get("/users/")
async def read_users():
return [{"username": "Rick"}, {"username": "Morty"}]

And all the ideas in the section about adding dependencies to the path operation decorators ↪ still
apply, but in this case, to all of the path operations in the app.

Dependencies for groups of path operations


Later, when reading about how to structure bigger applications (Bigger Applications - Multiple
Files ↪), possibly with multiple files, you will learn how to declare a single dependencies parameter

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
for a group of path operations.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Dependencies with yield

FastAPI supports dependencies that do some extra steps after finishing.

To do this, use yield instead of return , and write the extra steps (code) after.

Tip

Make sure to use yield one single time.

Technical Details

Any function that is valid to use with:

@contextlib.contextmanager [↪] or

@contextlib.asynccontextmanager [↪]

would be valid to use as a FastAPI dependency.

In fact, FastAPI uses those two decorators internally.

A database dependency with yield


For example, you could use this to create a database session and close it after finishing.

Only the code prior to and including the yield statement is executed before creating a response:

async def get_db():


db = DBSession()
try:
yield db
finally:
db.close()

The yielded value is what is injected into path operations and other dependencies:

async def get_db():


db = DBSession()
try:
yield db
finally:
db.close()

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
The code following the yield statement is executed after the response has been delivered:

async def get_db():


db = DBSession()
try:
yield db
finally:
db.close()

Tip

You can use async or regular functions.

FastAPI will do the right thing with each, the same as with normal dependencies.

A dependency with yield and try


If you use a try block in a dependency with yield , you'll receive any exception that was thrown
when using the dependency.

For example, if some code at some point in the middle, in another dependency or in a path operation,
made a database transaction "rollback" or create any other error, you will receive the exception in
your dependency.

So, you can look for that specific exception inside the dependency with except SomeException .

In the same way, you can use finally to make sure the exit steps are executed, no matter if there
was an exception or not.

async def get_db():


db = DBSession()
try:
yield db
finally:
db.close()

Sub-dependencies with yield


You can have sub-dependencies and "trees" of sub-dependencies of any size and shape, and any or
all of them can use yield .

FastAPI will make sure that the "exit code" in each dependency with yield is run in the correct
order.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
For example, dependency_c can have a dependency on dependency_b , and dependency_b on
dependency_a :

Python 3.9+ Python 3.8+ Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Depends

async def dependency_a():


dep_a = generate_dep_a()
try:
yield dep_a
finally:
dep_a.close()

async def dependency_b(dep_a: Annotated[DepA, Depends(dependency_a)]):


dep_b = generate_dep_b()
try:
yield dep_b
finally:
dep_b.close(dep_a)

async def dependency_c(dep_b: Annotated[DepB, Depends(dependency_b)]):


dep_c = generate_dep_c()
try:
yield dep_c
finally:
dep_c.close(dep_b)

And all of them can use yield .

In this case dependency_c , to execute its exit code, needs the value from dependency_b (here
named dep_b ) to still be available.

And, in turn, dependency_b needs the value from dependency_a (here named dep_a ) to be
available for its exit code.

Python 3.9+ Python 3.8+ Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Depends

async def dependency_a():


dep_a = generate_dep_a()

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
try:
yield dep_a
finally:
dep_a.close()

async def dependency_b(dep_a: Annotated[DepA, Depends(dependency_a)]):


dep_b = generate_dep_b()
try:
yield dep_b
finally:
dep_b.close(dep_a)

async def dependency_c(dep_b: Annotated[DepB, Depends(dependency_b)]):


dep_c = generate_dep_c()
try:
yield dep_c
finally:
dep_c.close(dep_b)

The same way, you could have some dependencies with yield and some other dependencies with
return , and have some of those depend on some of the others.

And you could have a single dependency that requires several other dependencies with yield , etc.

You can have any combinations of dependencies that you want.

FastAPI will make sure everything is run in the correct order.

Technical Details

This works thanks to Python's Context Managers [↪].

FastAPI uses them internally to achieve this.

Dependencies with yield and HTTPException


You saw that you can use dependencies with yield and have try blocks that catch exceptions.

The same way, you could raise an HTTPException or similar in the exit code, after the yield .

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Tip

This is a somewhat advanced technique, and in most of the cases you won't really need it, as you can raise exceptions
(including HTTPException ) from inside of the rest of your application code, for example, in the path operation function.

But it's there for you if you need it. 🤓


Python 3.9+ Python 3.8+ Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI, HTTPException

app = FastAPI()

data = {
"plumbus": {"description": "Freshly pickled plumbus", "owner": "Morty"},
"portal-gun": {"description": "Gun to create portals", "owner": "Rick"},
}

class OwnerError(Exception):
pass

def get_username():
try:
yield "Rick"
except OwnerError as e:
raise HTTPException(status_code=400, detail=f"Owner error: {e}")

@app.get("/items/{item_id}")
def get_item(item_id: str, username: Annotated[str, Depends(get_username)]):
if item_id not in data:
raise HTTPException(status_code=404, detail="Item not found")
item = data[item_id]
if item["owner"] != username:
raise OwnerError(username)
return item

An alternative you could use to catch exceptions (and possibly also raise another HTTPException )
is to create a Custom Exception Handler ↪.

Dependencies with yield and except

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
If you catch an exception using except in a dependency with yield and you don't raise it again (or
raise a new exception), FastAPI won't be able to notice there was an exception, the same way that
would happen with regular Python:

Python 3.9+ Python 3.8+ Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI, HTTPException

app = FastAPI()

class InternalError(Exception):
pass

def get_username():
try:
yield "Rick"
except InternalError:
print("Oops, we didn't raise again, Britney 😱")
@app.get("/items/{item_id}")
def get_item(item_id: str, username: Annotated[str, Depends(get_username)]):
if item_id == "portal-gun":
raise InternalError(
f"The portal gun is too dangerous to be owned by {username}"
)
if item_id != "plumbus":
raise HTTPException(
status_code=404, detail="Item not found, there's only a plumbus here"
)
return item_id

In this case, the client will see an HTTP 500 Internal Server Error response as it should, given that we
are not raising an HTTPException or similar, but the server will not have any logs or any other
indication of what was the error. 😱
Always raise in Dependencies with yield and except

If you catch an exception in a dependency with yield , unless you are raising another
HTTPException or similar, you should re-raise the original exception.

You can re-raise the same exception using raise :

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Python 3.9+ Python 3.8+ Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI, HTTPException

app = FastAPI()

class InternalError(Exception):
pass

def get_username():
try:
yield "Rick"
except InternalError:
print("We don't swallow the internal error here, we raise again 😎")
raise

@app.get("/items/{item_id}")
def get_item(item_id: str, username: Annotated[str, Depends(get_username)]):
if item_id == "portal-gun":
raise InternalError(
f"The portal gun is too dangerous to be owned by {username}"
)
if item_id != "plumbus":
raise HTTPException(
status_code=404, detail="Item not found, there's only a plumbus here"
)
return item_id

Now the client will get the same HTTP 500 Internal Server Error response, but the server will have our
custom InternalError in the logs. 😎
Execution of dependencies with yield
The sequence of execution is more or less like this diagram. Time flows from top to bottom. And
each column is one of the parts interacting or executing code.

Info

Only one response will be sent to the client. It might be one of the error responses or it will be the response from the path
operation.

After one of those responses is sent, no other response can be sent.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Tip

This diagram shows HTTPException , but you could also raise any other exception that you catch in a dependency with
yield or with a Custom Exception Handler ↪.

If you raise any exception, it will be passed to the dependencies with yield, including HTTPException . In most cases you
will want to re-raise that same exception or a new one from the dependency with yield to make sure it's properly
handled.

Dependencies with yield , HTTPException , except and


Background Tasks

Warning

You most probably don't need these technical details, you can skip this section and continue below.

These details are useful mainly if you were using a version of FastAPI prior to 0.106.0 and used resources from
dependencies with yield in background tasks.

Dependencies with yield and except , Technical Details

Before FastAPI 0.110.0, if you used a dependency with yield , and then you captured an exception
with except in that dependency, and you didn't raise the exception again, the exception would be
automatically raised/forwarded to any exception handlers or the internal server error handler.

This was changed in version 0.110.0 to fix unhandled memory consumption from forwarded
exceptions without a handler (internal server errors), and to make it consistent with the behavior of
regular Python code.

Background Tasks and Dependencies with yield , Technical Details

Before FastAPI 0.106.0, raising exceptions after yield was not possible, the exit code in
dependencies with yield was executed after the response was sent, so Exception Handlers ↪
would have already run.

This was designed this way mainly to allow using the same objects "yielded" by dependencies inside
of background tasks, because the exit code would be executed after the background tasks were
finished.

Nevertheless, as this would mean waiting for the response to travel through the network while
unnecessarily holding a resource in a dependency with yield (for example a database connection),

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
this was changed in FastAPI 0.106.0.

Tip

Additionally, a background task is normally an independent set of logic that should be handled separately, with its own
resources (e.g. its own database connection).

So, this way you will probably have cleaner code.

If you used to rely on this behavior, now you should create the resources for background tasks inside
the background task itself, and use internally only data that doesn't depend on the resources of
dependencies with yield .

For example, instead of using the same database session, you would create a new database
session inside of the background task, and you would obtain the objects from the database using
this new session. And then instead of passing the object from the database as a parameter to the
background task function, you would pass the ID of that object and then obtain the object again
inside the background task function.

Context Managers
What are "Context Managers"

"Context Managers" are any of those Python objects that you can use in a with statement.

For example, you can use with to read a file [↪]:

with open("./somefile.txt") as f:
contents = f.read()
print(contents)

Underneath, the open("./somefile.txt") creates an object that is called a "Context Manager".

When the with block finishes, it makes sure to close the file, even if there were exceptions.

When you create a dependency with yield , FastAPI will internally create a context manager for it,
and combine it with some other related tools.

Using context managers in dependencies with yield

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Warning

This is, more or less, an "advanced" idea.

If you are just starting with FastAPI you might want to skip it for now.

In Python, you can create Context Managers by


creating a class with two methods: __enter__() and __exit__() [↪].

You can also use them inside of FastAPI dependencies with yield by using with or async with
statements inside of the dependency function:

class MySuperContextManager:
def __init__(self):
self.db = DBSession()

def __enter__(self):
return self.db

def __exit__(self, exc_type, exc_value, traceback):


self.db.close()

async def get_db():


with MySuperContextManager() as db:
yield db

Tip

Another way to create a context manager is with:

@contextlib.contextmanager [↪] or

@contextlib.asynccontextmanager [↪]

using them to decorate a function with a single yield .

That's what FastAPI uses internally for dependencies with yield .

But you don't have to use the decorators for FastAPI dependencies (and you shouldn't).

FastAPI will do it for you internally.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Security

There are many ways to handle security, authentication and authorization.

And it normally is a complex and "difficult" topic.

In many frameworks and systems just handling security and authentication takes a big amount of
effort and code (in many cases it can be 50% or more of all the code written).

FastAPI provides several tools to help you deal with Security easily, rapidly, in a standard way,
without having to study and learn all the security specifications.

But first, let's check some small concepts.

In a hurry?
If you don't care about any of these terms and you just need to add security with authentication
based on username and password right now, skip to the next chapters.

OAuth2
OAuth2 is a specification that defines several ways to handle authentication and authorization.

It is quite an extensive specification and covers several complex use cases.

It includes ways to authenticate using a "third party".

That's what all the systems with "login with Facebook, Google, Twitter, GitHub" use underneath.

OAuth 1

There was an OAuth 1, which is very different from OAuth2, and more complex, as it included direct
specifications on how to encrypt the communication.

It is not very popular or used nowadays.

OAuth2 doesn't specify how to encrypt the communication, it expects you to have your application
served with HTTPS.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Tip

In the section about deployment you will see how to set up HTTPS for free, using Traefik and Let's Encrypt.

OpenID Connect
OpenID Connect is another specification, based on OAuth2.

It just extends OAuth2 specifying some things that are relatively ambiguous in OAuth2, to try to
make it more interoperable.

For example, Google login uses OpenID Connect (which underneath uses OAuth2).

But Facebook login doesn't support OpenID Connect. It has its own flavor of OAuth2.

OpenID (not "OpenID Connect")

There was also an "OpenID" specification. That tried to solve the same thing as OpenID Connect,
but was not based on OAuth2.

So, it was a complete additional system.

It is not very popular or used nowadays.

OpenAPI
OpenAPI (previously known as Swagger) is the open specification for building APIs (now part of
the Linux Foundation).

FastAPI is based on OpenAPI.

That's what makes it possible to have multiple automatic interactive documentation interfaces,
code generation, etc.

OpenAPI has a way to define multiple security "schemes".

By using them, you can take advantage of all these standard-based tools, including these
interactive documentation systems.

OpenAPI defines the following security schemes:

apiKey : an application specific key that can come from:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
A query parameter.

A header.

A cookie.

http : standard HTTP authentication systems, including:

bearer : a header Authorization with a value of Bearer plus a token. This is inherited
from OAuth2.

HTTP Basic authentication.

HTTP Digest, etc.

oauth2 : all the OAuth2 ways to handle security (called "flows").

Several of these flows are appropriate for building an OAuth 2.0 authentication provider
(like Google, Facebook, Twitter, GitHub, etc):

implicit

clientCredentials

authorizationCode

But there is one specific "flow" that can be perfectly used for handling authentication in the
same application directly:

password : some next chapters will cover examples of this.

openIdConnect : has a way to define how to discover OAuth2 authentication data


automatically.

This automatic discovery is what is defined in the OpenID Connect specification.

Tip

Integrating other authentication/authorization providers like Google, Facebook, Twitter, GitHub, etc. is also possible and
relatively easy.

The most complex problem is building an authentication/authorization provider like those, but FastAPI gives you the
tools to do it easily, while doing the heavy lifting for you.

FastAPI utilities
FastAPI provides several tools for each of these security schemes in the fastapi.security
module that simplify using these security mechanisms.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
In the next chapters you will see how to add security to your API using those tools provided by
FastAPI.

And you will also see how it gets automatically integrated into the interactive documentation
system.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Security - First Steps

Let's imagine that you have your backend API in some domain.

And you have a frontend in another domain or in a different path of the same domain (or in a mobile
application).

And you want to have a way for the frontend to authenticate with the backend, using a username and
password.

We can use OAuth2 to build that with FastAPI.

But let's save you the time of reading the full long specification just to find those little pieces of information
you need.

Let's use the tools provided by FastAPI to handle security.

How it looks
Let's first just use the code and see how it works, and then we'll come back to understand what's
happening.

Create main.py
Copy the example in a file main.py :

Python 3.9+ Python 3.8+ Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI


from fastapi.security import OAuth2PasswordBearer

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
return {"token": token}

Run it

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Info

The python-multipart [↪] package is automatically installed with FastAPI when you run the pip install fastapi command.

However, if you use the pip install fastapi-slim command, the python-multipart package is not included by default. To
install it manually, use the following command:

pip install python-multipart

This is because OAuth2 uses "form data" for sending the username and password .

Run the example with:

bash

fast →
$ fasta ▋

Check it
Go to the interactive docs at: http://127.0.0.1:8000/docs [↪].

You will see something like this:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Authorize button!

You already have a shiny new "Authorize" button.

And your path operation has a little lock in the top-right corner that you can click.

And if you click it, you have a little authorization form to type a username and password (and other
optional fields):

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Note

It doesn't matter what you type in the form, it won't work yet. But we'll get there.

This is of course not the frontend for the final users, but it's a great automatic tool to document
interactively all your API.

It can be used by the frontend team (that can also be yourself).

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
It can be used by third party applications and systems.

And it can also be used by yourself, to debug, check and test the same application.

The password flow


Now let's go back a bit and understand what is all that.

The password "flow" is one of the ways ("flows") defined in OAuth2, to handle security and authentication.

OAuth2 was designed so that the backend or API could be independent of the server that authenticates the
user.

But in this case, the same FastAPI application will handle the API and the authentication.

So, let's review it from that simplified point of view:

The user types the username and password in the frontend, and hits Enter .

The frontend (running in the user's browser) sends that username and password to a specific URL in
our API (declared with tokenUrl="token" ).

The API checks that username and password , and responds with a "token" (we haven't implemented
any of this yet).

A "token" is just a string with some content that we can use later to verify this user.

Normally, a token is set to expire after some time.

So, the user will have to log in again at some point later.

And if the token is stolen, the risk is less. It is not like a permanent key that will work forever
(in most of the cases).

The frontend stores that token temporarily somewhere.

The user clicks in the frontend to go to another section of the frontend web app.

The frontend needs to fetch some more data from the API.

But it needs authentication for that specific endpoint.

So, to authenticate with our API, it sends a header Authorization with a value of Bearer plus the
token.

If the token contains foobar , the content of the Authorization header would be:
Bearer foobar .

FastAPI's OAuth2PasswordBearer
FastAPI provides several tools, at different levels of abstraction, to implement these security features.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
In this example we are going to use OAuth2, with the Password flow, using a Bearer token. We do that
using the OAuth2PasswordBearer class.

Info

A "bearer" token is not the only option.

But it's the best one for our use case.

And it might be the best for most use cases, unless you are an OAuth2 expert and know exactly why there's another option that
suits better your needs.

In that case, FastAPI also provides you with the tools to build it.

When we create an instance of the OAuth2PasswordBearer class we pass in the tokenUrl parameter.
This parameter contains the URL that the client (the frontend running in the user's browser) will use to send
the username and password in order to get a token.

Python 3.9+ Python 3.8+ Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI


from fastapi.security import OAuth2PasswordBearer

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
return {"token": token}

Tip

Here tokenUrl="token" refers to a relative URL token that we haven't created yet. As it's a relative URL, it's equivalent to
./token .

Because we are using a relative URL, if your API was located at https://example.com/ , then it would refer to
https://example.com/token . But if your API was located at https://example.com/api/v1/ , then it would refer to
https://example.com/api/v1/token .

Using a relative URL is important to make sure your application keeps working even in an advanced use case like Behind a
Proxy ↪.

This parameter doesn't create that endpoint / path operation, but declares that the URL /token will be the
one that the client should use to get the token. That information is used in OpenAPI, and then in the
interactive API documentation systems.

We will soon also create the actual path operation.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Info

If you are a very strict "Pythonista" you might dislike the style of the parameter name tokenUrl instead of token_url .

That's because it is using the same name as in the OpenAPI spec. So that if you need to investigate more about any of these
security schemes you can just copy and paste it to find more information about it.

The oauth2_scheme variable is an instance of OAuth2PasswordBearer , but it is also a "callable".

It could be called as:

oauth2_scheme(some, parameters)

So, it can be used with Depends .

Use it

Now you can pass that oauth2_scheme in a dependency with Depends .

Python 3.9+ Python 3.8+ Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI


from fastapi.security import OAuth2PasswordBearer

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
return {"token": token}

This dependency will provide a str that is assigned to the parameter token of the path operation
function.

FastAPI will know that it can use this dependency to define a "security scheme" in the OpenAPI schema
(and the automatic API docs).

Technical Details

FastAPI will know that it can use the class OAuth2PasswordBearer (declared in a dependency) to define the security scheme in
OpenAPI because it inherits from fastapi.security.oauth2.OAuth2 , which in turn inherits from
fastapi.security.base.SecurityBase .

All the security utilities that integrate with OpenAPI (and the automatic API docs) inherit from SecurityBase , that's how FastAPI
can know how to integrate them in OpenAPI.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
What it does
It will go and look in the request for that Authorization header, check if the value is Bearer plus some
token, and will return the token as a str .

If it doesn't see an Authorization header, or the value doesn't have a Bearer token, it will respond with a
401 status code error ( UNAUTHORIZED ) directly.

You don't even have to check if the token exists to return an error. You can be sure that if your function is
executed, it will have a str in that token.

You can try it already in the interactive docs:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
We are not verifying the validity of the token yet, but that's a start already.

Recap
So, in just 3 or 4 extra lines, you already have some primitive form of security.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Get Current User

In the previous chapter the security system (which is based on the dependency injection system) was
giving the path operation function a token as a str :

Python 3.9+ Python 3.8+ Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI


from fastapi.security import OAuth2PasswordBearer

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

@app.get("/items/")
async def read_items(token: Annotated[str, Depends(oauth2_scheme)]):
return {"token": token}

But that is still not that useful.

Let's make it give us the current user.

Create a user model


First, let's create a Pydantic user model.

The same way we use Pydantic to declare bodies, we can use it anywhere else:

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI


from fastapi.security import OAuth2PasswordBearer
from pydantic import BaseModel

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

class User(BaseModel):
username: str
email: str | None = None

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
full_name: str | None = None
disabled: bool | None = None

def fake_decode_token(token):
return User(
username=token + "fakedecoded", email="[email protected]", full_name="John Doe"
)

async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):


user = fake_decode_token(token)
return user

@app.get("/users/me")
async def read_users_me(current_user: Annotated[User, Depends(get_current_user)]):
return current_user

Create a get_current_user dependency


Let's create a dependency get_current_user .

Remember that dependencies can have sub-dependencies?

get_current_user will have a dependency with the same oauth2_scheme we created before.

The same as we were doing before in the path operation directly, our new dependency
get_current_user will receive a token as a str from the sub-dependency oauth2_scheme :

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI


from fastapi.security import OAuth2PasswordBearer
from pydantic import BaseModel

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

class User(BaseModel):
username: str
email: str | None = None
full_name: str | None = None
disabled: bool | None = None

def fake_decode_token(token):

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
return User(
username=token + "fakedecoded", email="[email protected]", full_name="John Doe"
)

async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):


user = fake_decode_token(token)
return user

@app.get("/users/me")
async def read_users_me(current_user: Annotated[User, Depends(get_current_user)]):
return current_user

Get the user


get_current_user will use a (fake) utility function we created, that takes a token as a str and returns
our Pydantic User model:

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI


from fastapi.security import OAuth2PasswordBearer
from pydantic import BaseModel

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

class User(BaseModel):
username: str
email: str | None = None
full_name: str | None = None
disabled: bool | None = None

def fake_decode_token(token):
return User(
username=token + "fakedecoded", email="[email protected]", full_name="John Doe"
)

async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):


user = fake_decode_token(token)
return user

@app.get("/users/me")

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
async def read_users_me(current_user: Annotated[User, Depends(get_current_user)]):
return current_user

Inject the current user


So now we can use the same Depends with our get_current_user in the path operation:

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI


from fastapi.security import OAuth2PasswordBearer
from pydantic import BaseModel

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

class User(BaseModel):
username: str
email: str | None = None
full_name: str | None = None
disabled: bool | None = None

def fake_decode_token(token):
return User(
username=token + "fakedecoded", email="[email protected]", full_name="John Doe"
)

async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):


user = fake_decode_token(token)
return user

@app.get("/users/me")
async def read_users_me(current_user: Annotated[User, Depends(get_current_user)]):
return current_user

Notice that we declare the type of current_user as the Pydantic model User .

This will help us inside of the function with all the completion and type checks.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Tip

You might remember that request bodies are also declared with Pydantic models.

Here FastAPI won't get confused because you are using Depends .

Check

The way this dependency system is designed allows us to have different dependencies (different "dependables") that all return
a User model.

We are not restricted to having only one dependency that can return that type of data.

Other models
You can now get the current user directly in the path operation functions and deal with the security
mechanisms at the Dependency Injection level, using Depends .

And you can use any model or data for the security requirements (in this case, a Pydantic model User ).

But you are not restricted to using some specific data model, class or type.

Do you want to have an id and email and not have any username in your model? Sure. You can use
these same tools.

Do you want to just have a str ? Or just a dict ? Or a database class model instance directly? It all
works the same way.

You actually don't have users that log in to your application but robots, bots, or other systems, that have
just an access token? Again, it all works the same.

Just use any kind of model, any kind of class, any kind of database that you need for your application.
FastAPI has you covered with the dependency injection system.

Code size
This example might seem verbose. Keep in mind that we are mixing security, data models, utility
functions and path operations in the same file.

But here's the key point.

The security and dependency injection stuff is written once.

And you can make it as complex as you want. And still, have it written only once, in a single place. With
all the flexibility.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
But you can have thousands of endpoints (path operations) using the same security system.

And all of them (or any portion of them that you want) can take the advantage of re-using these
dependencies or any other dependencies you create.

And all these thousands of path operations can be as small as 3 lines:

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI


from fastapi.security import OAuth2PasswordBearer
from pydantic import BaseModel

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

class User(BaseModel):
username: str
email: str | None = None
full_name: str | None = None
disabled: bool | None = None

def fake_decode_token(token):
return User(
username=token + "fakedecoded", email="[email protected]", full_name="John Doe"
)

async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):


user = fake_decode_token(token)
return user

@app.get("/users/me")
async def read_users_me(current_user: Annotated[User, Depends(get_current_user)]):
return current_user

Recap
You can now get the current user directly in your path operation function.

We are already halfway there.

We just need to add a path operation for the user/client to actually send the username and password .

That comes next.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Simple OAuth2 with Password and Bearer

Now let's build from the previous chapter and add the missing parts to have a complete security flow.

Get the username and password


We are going to use FastAPI security utilities to get the username and password .

OAuth2 specifies that when using the "password flow" (that we are using) the client/user must send a
username and password fields as form data.

And the spec says that the fields have to be named like that. So user-name or email wouldn't work.

But don't worry, you can show it as you wish to your final users in the frontend.

And your database models can use any other names you want.

But for the login path operation, we need to use these names to be compatible with the spec (and be
able to, for example, use the integrated API documentation system).

The spec also states that the username and password must be sent as form data (so, no JSON here).

scope

The spec also says that the client can send another form field " scope ".

The form field name is scope (in singular), but it is actually a long string with "scopes" separated by
spaces.

Each "scope" is just a string (without spaces).

They are normally used to declare specific security permissions, for example:

users:read or users:write are common examples.

instagram_basic is used by Facebook / Instagram.

https://www.googleapis.com/auth/drive is used by Google.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Info

In OAuth2 a "scope" is just a string that declares a specific permission required.

It doesn't matter if it has other characters like : or if it is a URL.

Those details are implementation specific.

For OAuth2 they are just strings.

Code to get the username and password


Now let's use the utilities provided by FastAPI to handle this.

OAuth2PasswordRequestForm

First, import OAuth2PasswordRequestForm , and use it as a dependency with Depends in the path
operation for /token :

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI, HTTPException, status


from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel

fake_users_db = {
"johndoe": {
"username": "johndoe",
"full_name": "John Doe",
"email": "[email protected]",
"hashed_password": "fakehashedsecret",
"disabled": False,
},
"alice": {
"username": "alice",
"full_name": "Alice Wonderson",
"email": "[email protected]",
"hashed_password": "fakehashedsecret2",
"disabled": True,
},
}

app = FastAPI()

def fake_hash_password(password: str):


return "fakehashed" + password

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

class User(BaseModel):
username: str
email: str | None = None
full_name: str | None = None
disabled: bool | None = None

class UserInDB(User):
hashed_password: str

def get_user(db, username: str):


if username in db:
user_dict = db[username]
return UserInDB(**user_dict)

def fake_decode_token(token):
# This doesn't provide any security at all
# Check the next version
user = get_user(fake_users_db, token)
return user

async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):


user = fake_decode_token(token)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authentication credentials",
headers={"WWW-Authenticate": "Bearer"},
)
return user

async def get_current_active_user(


current_user: Annotated[User, Depends(get_current_user)],
):
if current_user.disabled:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user

@app.post("/token")
async def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]):
user_dict = fake_users_db.get(form_data.username)
if not user_dict:
raise HTTPException(status_code=400, detail="Incorrect username or password")
user = UserInDB(**user_dict)
hashed_password = fake_hash_password(form_data.password)
if not hashed_password == user.hashed_password:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
raise HTTPException(status_code=400, detail="Incorrect username or password")

return {"access_token": user.username, "token_type": "bearer"}

@app.get("/users/me")
async def read_users_me(
current_user: Annotated[User, Depends(get_current_active_user)],
):
return current_user

OAuth2PasswordRequestForm is a class dependency that declares a form body with:

The username .

The password .

An optional scope field as a big string, composed of strings separated by spaces.

An optional grant_type .

Tip

The OAuth2 spec actually requires a field grant_type with a fixed value of password , but OAuth2PasswordRequestForm
doesn't enforce it.

If you need to enforce it, use OAuth2PasswordRequestFormStrict instead of OAuth2PasswordRequestForm .

An optional client_id (we don't need it for our example).

An optional client_secret (we don't need it for our example).

Info

The OAuth2PasswordRequestForm is not a special class for FastAPI as is OAuth2PasswordBearer .

OAuth2PasswordBearer makes FastAPI know that it is a security scheme. So it is added that way to OpenAPI.

But OAuth2PasswordRequestForm is just a class dependency that you could have written yourself, or you could have declared
Form parameters directly.

But as it's a common use case, it is provided by FastAPI directly, just to make it easier.

Use the form data

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Tip

The instance of the dependency class OAuth2PasswordRequestForm won't have an attribute scope with the long string
separated by spaces, instead, it will have a scopes attribute with the actual list of strings for each scope sent.

We are not using scopes in this example, but the functionality is there if you need it.

Now, get the user data from the (fake) database, using the username from the form field.

If there is no such user, we return an error saying "Incorrect username or password".

For the error, we use the exception HTTPException :

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI, HTTPException, status


from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel

fake_users_db = {
"johndoe": {
"username": "johndoe",
"full_name": "John Doe",
"email": "[email protected]",
"hashed_password": "fakehashedsecret",
"disabled": False,
},
"alice": {
"username": "alice",
"full_name": "Alice Wonderson",
"email": "[email protected]",
"hashed_password": "fakehashedsecret2",
"disabled": True,
},
}

app = FastAPI()

def fake_hash_password(password: str):


return "fakehashed" + password

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

class User(BaseModel):
username: str
email: str | None = None
full_name: str | None = None

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
disabled: bool | None = None

class UserInDB(User):
hashed_password: str

def get_user(db, username: str):


if username in db:
user_dict = db[username]
return UserInDB(**user_dict)

def fake_decode_token(token):
# This doesn't provide any security at all
# Check the next version
user = get_user(fake_users_db, token)
return user

async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):


user = fake_decode_token(token)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authentication credentials",
headers={"WWW-Authenticate": "Bearer"},
)
return user

async def get_current_active_user(


current_user: Annotated[User, Depends(get_current_user)],
):
if current_user.disabled:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user

@app.post("/token")
async def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]):
user_dict = fake_users_db.get(form_data.username)
if not user_dict:
raise HTTPException(status_code=400, detail="Incorrect username or password")
user = UserInDB(**user_dict)
hashed_password = fake_hash_password(form_data.password)
if not hashed_password == user.hashed_password:
raise HTTPException(status_code=400, detail="Incorrect username or password")

return {"access_token": user.username, "token_type": "bearer"}

@app.get("/users/me")
async def read_users_me(
current_user: Annotated[User, Depends(get_current_active_user)],

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
):
return current_user

Check the password

At this point we have the user data from our database, but we haven't checked the password.

Let's put that data in the Pydantic UserInDB model first.

You should never save plaintext passwords, so, we'll use the (fake) password hashing system.

If the passwords don't match, we return the same error.

Password hashing

"Hashing" means: converting some content (a password in this case) into a sequence of bytes (just a
string) that looks like gibberish.

Whenever you pass exactly the same content (exactly the same password) you get exactly the same
gibberish.

But you cannot convert from the gibberish back to the password.

WHY USE PASSWORD HASHING

If your database is stolen, the thief won't have your users' plaintext passwords, only the hashes.

So, the thief won't be able to try to use those same passwords in another system (as many users use
the same password everywhere, this would be dangerous).

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI, HTTPException, status


from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel

fake_users_db = {
"johndoe": {
"username": "johndoe",
"full_name": "John Doe",
"email": "[email protected]",
"hashed_password": "fakehashedsecret",
"disabled": False,
},
"alice": {
"username": "alice",
"full_name": "Alice Wonderson",
"email": "[email protected]",

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
"hashed_password": "fakehashedsecret2",
"disabled": True,
},
}

app = FastAPI()

def fake_hash_password(password: str):


return "fakehashed" + password

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

class User(BaseModel):
username: str
email: str | None = None
full_name: str | None = None
disabled: bool | None = None

class UserInDB(User):
hashed_password: str

def get_user(db, username: str):


if username in db:
user_dict = db[username]
return UserInDB(**user_dict)

def fake_decode_token(token):
# This doesn't provide any security at all
# Check the next version
user = get_user(fake_users_db, token)
return user

async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):


user = fake_decode_token(token)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authentication credentials",
headers={"WWW-Authenticate": "Bearer"},
)
return user

async def get_current_active_user(


current_user: Annotated[User, Depends(get_current_user)],
):
if current_user.disabled:
raise HTTPException(status_code=400, detail="Inactive user")

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
return current_user

@app.post("/token")
async def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]):
user_dict = fake_users_db.get(form_data.username)
if not user_dict:
raise HTTPException(status_code=400, detail="Incorrect username or password")
user = UserInDB(**user_dict)
hashed_password = fake_hash_password(form_data.password)
if not hashed_password == user.hashed_password:
raise HTTPException(status_code=400, detail="Incorrect username or password")

return {"access_token": user.username, "token_type": "bearer"}

@app.get("/users/me")
async def read_users_me(
current_user: Annotated[User, Depends(get_current_active_user)],
):
return current_user

About **user_dict

UserInDB(**user_dict) means:

Pass the keys and values of the user_dict directly as key-value arguments, equivalent to:

UserInDB(
username = user_dict["username"],
email = user_dict["email"],
full_name = user_dict["full_name"],
disabled = user_dict["disabled"],
hashed_password = user_dict["hashed_password"],
)

Info

For a more complete explanation of **user_dict check back in the documentation for Extra Models ↪.

Return the token


The response of the token endpoint must be a JSON object.

It should have a token_type . In our case, as we are using "Bearer" tokens, the token type should be "
bearer ".

And it should have an access_token , with a string containing our access token.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
For this simple example, we are going to just be completely insecure and return the same username as
the token.

Tip

In the next chapter, you will see a real secure implementation, with password hashing and JWT tokens.

But for now, let's focus on the specific details we need.

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI, HTTPException, status


from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel

fake_users_db = {
"johndoe": {
"username": "johndoe",
"full_name": "John Doe",
"email": "[email protected]",
"hashed_password": "fakehashedsecret",
"disabled": False,
},
"alice": {
"username": "alice",
"full_name": "Alice Wonderson",
"email": "[email protected]",
"hashed_password": "fakehashedsecret2",
"disabled": True,
},
}

app = FastAPI()

def fake_hash_password(password: str):


return "fakehashed" + password

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

class User(BaseModel):
username: str
email: str | None = None
full_name: str | None = None
disabled: bool | None = None

class UserInDB(User):

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
hashed_password: str

def get_user(db, username: str):


if username in db:
user_dict = db[username]
return UserInDB(**user_dict)

def fake_decode_token(token):
# This doesn't provide any security at all
# Check the next version
user = get_user(fake_users_db, token)
return user

async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):


user = fake_decode_token(token)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authentication credentials",
headers={"WWW-Authenticate": "Bearer"},
)
return user

async def get_current_active_user(


current_user: Annotated[User, Depends(get_current_user)],
):
if current_user.disabled:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user

@app.post("/token")
async def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]):
user_dict = fake_users_db.get(form_data.username)
if not user_dict:
raise HTTPException(status_code=400, detail="Incorrect username or password")
user = UserInDB(**user_dict)
hashed_password = fake_hash_password(form_data.password)
if not hashed_password == user.hashed_password:
raise HTTPException(status_code=400, detail="Incorrect username or password")

return {"access_token": user.username, "token_type": "bearer"}

@app.get("/users/me")
async def read_users_me(
current_user: Annotated[User, Depends(get_current_active_user)],
):
return current_user

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Tip

By the spec, you should return a JSON with an access_token and a token_type , the same as in this example.

This is something that you have to do yourself in your code, and make sure you use those JSON keys.

It's almost the only thing that you have to remember to do correctly yourself, to be compliant with the specifications.

For the rest, FastAPI handles it for you.

Update the dependencies


Now we are going to update our dependencies.

We want to get the current_user only if this user is active.

So, we create an additional dependency get_current_active_user that in turn uses


get_current_user as a dependency.

Both of these dependencies will just return an HTTP error if the user doesn't exist, or if is inactive.

So, in our endpoint, we will only get a user if the user exists, was correctly authenticated, and is active:

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import Depends, FastAPI, HTTPException, status


from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel

fake_users_db = {
"johndoe": {
"username": "johndoe",
"full_name": "John Doe",
"email": "[email protected]",
"hashed_password": "fakehashedsecret",
"disabled": False,
},
"alice": {
"username": "alice",
"full_name": "Alice Wonderson",
"email": "[email protected]",
"hashed_password": "fakehashedsecret2",
"disabled": True,
},
}

app = FastAPI()

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
def fake_hash_password(password: str):
return "fakehashed" + password

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

class User(BaseModel):
username: str
email: str | None = None
full_name: str | None = None
disabled: bool | None = None

class UserInDB(User):
hashed_password: str

def get_user(db, username: str):


if username in db:
user_dict = db[username]
return UserInDB(**user_dict)

def fake_decode_token(token):
# This doesn't provide any security at all
# Check the next version
user = get_user(fake_users_db, token)
return user

async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):


user = fake_decode_token(token)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid authentication credentials",
headers={"WWW-Authenticate": "Bearer"},
)
return user

async def get_current_active_user(


current_user: Annotated[User, Depends(get_current_user)],
):
if current_user.disabled:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user

@app.post("/token")
async def login(form_data: Annotated[OAuth2PasswordRequestForm, Depends()]):
user_dict = fake_users_db.get(form_data.username)
if not user_dict:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
raise HTTPException(status_code=400, detail="Incorrect username or password")
user = UserInDB(**user_dict)
hashed_password = fake_hash_password(form_data.password)
if not hashed_password == user.hashed_password:
raise HTTPException(status_code=400, detail="Incorrect username or password")

return {"access_token": user.username, "token_type": "bearer"}

@app.get("/users/me")
async def read_users_me(
current_user: Annotated[User, Depends(get_current_active_user)],
):
return current_user

Info

The additional header WWW-Authenticate with value Bearer we are returning here is also part of the spec.

Any HTTP (error) status code 401 "UNAUTHORIZED" is supposed to also return a WWW-Authenticate header.

In the case of bearer tokens (our case), the value of that header should be Bearer .

You can actually skip that extra header and it would still work.

But it's provided here to be compliant with the specifications.

Also, there might be tools that expect and use it (now or in the future) and that might be useful for you or your users, now or
in the future.

That's the benefit of standards...

See it in action
Open the interactive docs: http://127.0.0.1:8000/docs [↪].

Authenticate

Click the "Authorize" button.

Use the credentials:

User: johndoe

Password: secret

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
After authenticating in the system, you will see it like:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Get your own user data

Now use the operation GET with the path /users/me .

You will get your user's data, like:

{
"username": "johndoe",
"email": "[email protected]",

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
"full_name": "John Doe",
"disabled": false,
"hashed_password": "fakehashedsecret"
}

If you click the lock icon and logout, and then try the same operation again, you will get an HTTP 401
error of:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
{
"detail": "Not authenticated"
}

Inactive user

Now try with an inactive user, authenticate with:

User: alice

Password: secret2

And try to use the operation GET with the path /users/me .

You will get an "Inactive user" error, like:

{
"detail": "Inactive user"
}

Recap
You now have the tools to implement a complete security system based on username and password
for your API.

Using these tools, you can make the security system compatible with any database and with any user
or data model.

The only detail missing is that it is not actually "secure" yet.

In the next chapter you'll see how to use a secure password hashing library and JWT tokens.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
OAuth2 with Password (and hashing), Bearer with JWT tokens

Now that we have all the security flow, let's make the application actually secure, using JWT tokens and secure password hashing.

This code is something you can actually use in your application, save the password hashes in your database, etc.

We are going to start from where we left in the previous chapter and increment it.

About JWT
JWT means "JSON Web Tokens".

It's a standard to codify a JSON object in a long dense string without spaces. It looks like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4f

It is not encrypted, so, anyone could recover the information from the contents.

But it's signed. So, when you receive a token that you emitted, you can verify that you actually emitted it.

That way, you can create a token with an expiration of, let's say, 1 week. And then when the user comes back the next day with the token, you know that
your system.

After a week, the token will be expired and the user will not be authorized and will have to sign in again to get a new token. And if the user (or a third part
to change the expiration, you would be able to discover it, because the signatures would not match.

If you want to play with JWT tokens and see how they work, check https://jwt.io [↪].

Install PyJWT
We need to install PyJWT to generate and verify the JWT tokens in Python:

bash

fast →
$ pip install pyjwt

Info

If you are planning to use digital signature algorithms like RSA or ECDSA, you should install the cryptography library dependency pyjwt[crypto] .

You can read more about it in the PyJWT Installation docs [↪].

Password hashing
"Hashing" means converting some content (a password in this case) into a sequence of bytes (just a string) that looks like gibberish.

Whenever you pass exactly the same content (exactly the same password) you get exactly the same gibberish.

But you cannot convert from the gibberish back to the password.

Why use password hashing

If your database is stolen, the thief won't have your users' plaintext passwords, only the hashes.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
So, the thief won't be able to try to use that password in another system (as many users use the same password everywhere, this would be dangerous).

Install passlib
PassLib is a great Python package to handle password hashes.

It supports many secure hashing algorithms and utilities to work with them.

The recommended algorithm is "Bcrypt".

So, install PassLib with Bcrypt:

bash

fast →
$ pip install "passlib[bc ▋

Tip

With passlib , you could even configure it to be able to read passwords created by Django, a Flask security plug-in or many others.

So, you would be able to, for example, share the same data from a Django application in a database with a FastAPI application. Or gradually migrate a Django application using the same da

And your users would be able to login from your Django app or from your FastAPI app, at the same time.

Hash and verify the passwords


Import the tools we need from passlib .

Create a PassLib "context". This is what will be used to hash and verify passwords.

Tip

The PassLib context also has functionality to use different hashing algorithms, including deprecated old ones only to allow verifying them, etc.

For example, you could use it to read and verify passwords generated by another system (like Django) but hash any new passwords with a different algorithm like Bcrypt.

And be compatible with all of them at the same time.

Create a utility function to hash a password coming from the user.

And another utility to verify if a received password matches the hash stored.

And another one to authenticate and return a user.

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from datetime import datetime, timedelta, timezone


from typing import Annotated

import jwt
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jwt.exceptions import InvalidTokenError
from passlib.context import CryptContext
from pydantic import BaseModel

# to get a string like this run:


# openssl rand -hex 32
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
fake_users_db = {
"johndoe": {
"username": "johndoe",
"full_name": "John Doe",
"email": "[email protected]",
"hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
"disabled": False,
}
}

class Token(BaseModel):
access_token: str
token_type: str

class TokenData(BaseModel):
username: str | None = None

class User(BaseModel):
username: str
email: str | None = None
full_name: str | None = None
disabled: bool | None = None

class UserInDB(User):
hashed_password: str

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

app = FastAPI()

def verify_password(plain_password, hashed_password):


return pwd_context.verify(plain_password, hashed_password)

def get_password_hash(password):
return pwd_context.hash(password)

def get_user(db, username: str):


if username in db:
user_dict = db[username]
return UserInDB(**user_dict)

def authenticate_user(fake_db, username: str, password: str):


user = get_user(fake_db, username)
if not user:
return False
if not verify_password(password, user.hashed_password):
return False
return user

def create_access_token(data: dict, expires_delta: timedelta | None = None):


to_encode = data.copy()
if expires_delta:
expire = datetime.now(timezone.utc) + expires_delta
else:
expire = datetime.now(timezone.utc) + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt

async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):


credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
token_data = TokenData(username=username)
except InvalidTokenError:
raise credentials_exception
user = get_user(fake_users_db, username=token_data.username)
if user is None:
raise credentials_exception
return user

async def get_current_active_user(


current_user: Annotated[User, Depends(get_current_user)],
):
if current_user.disabled:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user

@app.post("/token")
async def login_for_access_token(
form_data: Annotated[OAuth2PasswordRequestForm, Depends()],
) -> Token:
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.username}, expires_delta=access_token_expires
)
return Token(access_token=access_token, token_type="bearer")

@app.get("/users/me/", response_model=User)
async def read_users_me(
current_user: Annotated[User, Depends(get_current_active_user)],
):
return current_user

@app.get("/users/me/items/")
async def read_own_items(
current_user: Annotated[User, Depends(get_current_active_user)],
):
return [{"item_id": "Foo", "owner": current_user.username}]

Note

If you check the new (fake) database fake_users_db , you will see how the hashed password looks like now: "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW" .

Handle JWT tokens


Import the modules installed.

Create a random secret key that will be used to sign the JWT tokens.

To generate a secure random secret key use the command:

bash

fast →
$ openssl rand -hex 32 ▋

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
And copy the output to the variable SECRET_KEY (don't use the one in the example).

Create a variable ALGORITHM with the algorithm used to sign the JWT token and set it to "HS256" .

Create a variable for the expiration of the token.

Define a Pydantic Model that will be used in the token endpoint for the response.

Create a utility function to generate a new access token.

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from datetime import datetime, timedelta, timezone


from typing import Annotated

import jwt
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jwt.exceptions import InvalidTokenError
from passlib.context import CryptContext
from pydantic import BaseModel

# to get a string like this run:


# openssl rand -hex 32
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

fake_users_db = {
"johndoe": {
"username": "johndoe",
"full_name": "John Doe",
"email": "[email protected]",
"hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
"disabled": False,
}
}

class Token(BaseModel):
access_token: str
token_type: str

class TokenData(BaseModel):
username: str | None = None

class User(BaseModel):
username: str
email: str | None = None
full_name: str | None = None
disabled: bool | None = None

class UserInDB(User):
hashed_password: str

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

app = FastAPI()

def verify_password(plain_password, hashed_password):


return pwd_context.verify(plain_password, hashed_password)

def get_password_hash(password):
return pwd_context.hash(password)

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
def get_user(db, username: str):
if username in db:
user_dict = db[username]
return UserInDB(**user_dict)

def authenticate_user(fake_db, username: str, password: str):


user = get_user(fake_db, username)
if not user:
return False
if not verify_password(password, user.hashed_password):
return False
return user

def create_access_token(data: dict, expires_delta: timedelta | None = None):


to_encode = data.copy()
if expires_delta:
expire = datetime.now(timezone.utc) + expires_delta
else:
expire = datetime.now(timezone.utc) + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt

async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):


credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
token_data = TokenData(username=username)
except InvalidTokenError:
raise credentials_exception
user = get_user(fake_users_db, username=token_data.username)
if user is None:
raise credentials_exception
return user

async def get_current_active_user(


current_user: Annotated[User, Depends(get_current_user)],
):
if current_user.disabled:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user

@app.post("/token")
async def login_for_access_token(
form_data: Annotated[OAuth2PasswordRequestForm, Depends()],
) -> Token:
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.username}, expires_delta=access_token_expires
)
return Token(access_token=access_token, token_type="bearer")

@app.get("/users/me/", response_model=User)
async def read_users_me(
current_user: Annotated[User, Depends(get_current_active_user)],
):
return current_user

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
@app.get("/users/me/items/")
async def read_own_items(
current_user: Annotated[User, Depends(get_current_active_user)],
):
return [{"item_id": "Foo", "owner": current_user.username}]

Update the dependencies


Update get_current_user to receive the same token as before, but this time, using JWT tokens.

Decode the received token, verify it, and return the current user.

If the token is invalid, return an HTTP error right away.

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from datetime import datetime, timedelta, timezone


from typing import Annotated

import jwt
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jwt.exceptions import InvalidTokenError
from passlib.context import CryptContext
from pydantic import BaseModel

# to get a string like this run:


# openssl rand -hex 32
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

fake_users_db = {
"johndoe": {
"username": "johndoe",
"full_name": "John Doe",
"email": "[email protected]",
"hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
"disabled": False,
}
}

class Token(BaseModel):
access_token: str
token_type: str

class TokenData(BaseModel):
username: str | None = None

class User(BaseModel):
username: str
email: str | None = None
full_name: str | None = None
disabled: bool | None = None

class UserInDB(User):
hashed_password: str

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

app = FastAPI()

def verify_password(plain_password, hashed_password):


return pwd_context.verify(plain_password, hashed_password)

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
def get_password_hash(password):
return pwd_context.hash(password)

def get_user(db, username: str):


if username in db:
user_dict = db[username]
return UserInDB(**user_dict)

def authenticate_user(fake_db, username: str, password: str):


user = get_user(fake_db, username)
if not user:
return False
if not verify_password(password, user.hashed_password):
return False
return user

def create_access_token(data: dict, expires_delta: timedelta | None = None):


to_encode = data.copy()
if expires_delta:
expire = datetime.now(timezone.utc) + expires_delta
else:
expire = datetime.now(timezone.utc) + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt

async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):


credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
token_data = TokenData(username=username)
except InvalidTokenError:
raise credentials_exception
user = get_user(fake_users_db, username=token_data.username)
if user is None:
raise credentials_exception
return user

async def get_current_active_user(


current_user: Annotated[User, Depends(get_current_user)],
):
if current_user.disabled:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user

@app.post("/token")
async def login_for_access_token(
form_data: Annotated[OAuth2PasswordRequestForm, Depends()],
) -> Token:
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.username}, expires_delta=access_token_expires
)
return Token(access_token=access_token, token_type="bearer")

@app.get("/users/me/", response_model=User)
async def read_users_me(

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
current_user: Annotated[User, Depends(get_current_active_user)],
):
return current_user

@app.get("/users/me/items/")
async def read_own_items(
current_user: Annotated[User, Depends(get_current_active_user)],
):
return [{"item_id": "Foo", "owner": current_user.username}]

Update the /token path operation


Create a timedelta with the expiration time of the token.

Create a real JWT access token and return it.

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from datetime import datetime, timedelta, timezone


from typing import Annotated

import jwt
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jwt.exceptions import InvalidTokenError
from passlib.context import CryptContext
from pydantic import BaseModel

# to get a string like this run:


# openssl rand -hex 32
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30

fake_users_db = {
"johndoe": {
"username": "johndoe",
"full_name": "John Doe",
"email": "[email protected]",
"hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
"disabled": False,
}
}

class Token(BaseModel):
access_token: str
token_type: str

class TokenData(BaseModel):
username: str | None = None

class User(BaseModel):
username: str
email: str | None = None
full_name: str | None = None
disabled: bool | None = None

class UserInDB(User):
hashed_password: str

pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

app = FastAPI()

def verify_password(plain_password, hashed_password):


return pwd_context.verify(plain_password, hashed_password)

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
def get_password_hash(password):
return pwd_context.hash(password)

def get_user(db, username: str):


if username in db:
user_dict = db[username]
return UserInDB(**user_dict)

def authenticate_user(fake_db, username: str, password: str):


user = get_user(fake_db, username)
if not user:
return False
if not verify_password(password, user.hashed_password):
return False
return user

def create_access_token(data: dict, expires_delta: timedelta | None = None):


to_encode = data.copy()
if expires_delta:
expire = datetime.now(timezone.utc) + expires_delta
else:
expire = datetime.now(timezone.utc) + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt

async def get_current_user(token: Annotated[str, Depends(oauth2_scheme)]):


credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
token_data = TokenData(username=username)
except InvalidTokenError:
raise credentials_exception
user = get_user(fake_users_db, username=token_data.username)
if user is None:
raise credentials_exception
return user

async def get_current_active_user(


current_user: Annotated[User, Depends(get_current_user)],
):
if current_user.disabled:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user

@app.post("/token")
async def login_for_access_token(
form_data: Annotated[OAuth2PasswordRequestForm, Depends()],
) -> Token:
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.username}, expires_delta=access_token_expires
)
return Token(access_token=access_token, token_type="bearer")

@app.get("/users/me/", response_model=User)

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
async def read_users_me(
current_user: Annotated[User, Depends(get_current_active_user)],
):
return current_user

@app.get("/users/me/items/")
async def read_own_items(
current_user: Annotated[User, Depends(get_current_active_user)],
):
return [{"item_id": "Foo", "owner": current_user.username}]

Technical details about the JWT "subject" sub

The JWT specification says that there's a key sub , with the subject of the token.

It's optional to use it, but that's where you would put the user's identification, so we are using it here.

JWT might be used for other things apart from identifying a user and allowing them to perform operations directly on your API.

For example, you could identify a "car" or a "blog post".

Then you could add permissions about that entity, like "drive" (for the car) or "edit" (for the blog).

And then, you could give that JWT token to a user (or bot), and they could use it to perform those actions (drive the car, or edit the blog post) without eve
account, just with the JWT token your API generated for that.

Using these ideas, JWT can be used for way more sophisticated scenarios.

In those cases, several of those entities could have the same ID, let's say foo (a user foo , a car foo , and a blog post foo ).

So, to avoid ID collisions, when creating the JWT token for the user, you could prefix the value of the sub key, e.g. with username: . So, in this example, t
have been: username:johndoe .

The important thing to keep in mind is that the sub key should have a unique identifier across the entire application, and it should be a string.

Check it
Run the server and go to the docs: http://127.0.0.1:8000/docs [↪].

You'll see the user interface like:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Authorize the application the same way as before.

Using the credentials:

Username: johndoe Password: secret

Check

Notice that nowhere in the code is the plaintext password " secret ", we only have the hashed version.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Call the endpoint /users/me/ , you will get the response as:

{
"username": "johndoe",
"email": "[email protected]",
"full_name": "John Doe",
"disabled": false
}

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
If you open the developer tools, you could see how the data sent only includes the token, the password is only sent in the first request to authenticate th
token, but not afterwards:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Note

Notice the header Authorization , with a value that starts with Bearer .

Advanced usage with scopes


OAuth2 has the notion of "scopes".

You can use them to add a specific set of permissions to a JWT token.

Then you can give this token to a user directly or a third party, to interact with your API with a set of restrictions.

You can learn how to use them and how they are integrated into FastAPI later in the Advanced User Guide.

Recap

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
With what you have seen up to now, you can set up a secure FastAPI application using standards like OAuth2 and JWT.

In almost any framework handling the security becomes a rather complex subject quite quickly.

Many packages that simplify it a lot have to make many compromises with the data model, database, and available features. And some of these packag
much actually have security flaws underneath.

FastAPI doesn't make any compromise with any database, data model or tool.

It gives you all the flexibility to choose the ones that fit your project the best.

And you can use directly many well maintained and widely used packages like passlib and PyJWT , because FastAPI doesn't require any complex mech
external packages.

But it provides you the tools to simplify the process as much as possible without compromising flexibility, robustness, or security.

And you can use and implement secure, standard protocols, like OAuth2 in a relatively simple way.

You can learn more in the Advanced User Guide about how to use OAuth2 "scopes", for a more fine-grained permission system, following these same st
scopes is the mechanism used by many big authentication providers, like Facebook, Google, GitHub, Microsoft, Twitter, etc. to authorize third party appl
their APIs on behalf of their users.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Middleware

You can add middleware to FastAPI applications.

A "middleware" is a function that works with every request before it is processed by any specific
path operation. And also with every response before returning it.

It takes each request that comes to your application.

It can then do something to that request or run any needed code.

Then it passes the request to be processed by the rest of the application (by some path
operation).

It then takes the response generated by the application (by some path operation).

It can do something to that response or run any needed code.

Then it returns the response.

Technical Details

If you have dependencies with yield , the exit code will run after the middleware.

If there were any background tasks (documented later), they will run after all the middleware.

Create a middleware
To create a middleware you use the decorator @app.middleware("http") on top of a function.

The middleware function receives:

The request .

A function call_next that will receive the request as a parameter.

This function will pass the request to the corresponding path operation.

Then it returns the response generated by the corresponding path operation.

You can then modify further the response before returning it.

import time

from fastapi import FastAPI, Request

app = FastAPI()

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time
response.headers["X-Process-Time"] = str(process_time)
return response

Tip

Keep in mind that custom proprietary headers can be added using the 'X-' prefix [↪].

But if you have custom headers that you want a client in a browser to be able to see, you need to add them to your CORS
configurations (CORS (Cross-Origin Resource Sharing) ↪) using the parameter expose_headers documented in
Starlette's CORS docs [↪].

Technical Details

You could also use from starlette.requests import Request .

FastAPI provides it as a convenience for you, the developer. But it comes directly from Starlette.

Before and after the response

You can add code to be run with the request , before any path operation receives it.

And also after the response is generated, before returning it.

For example, you could add a custom header X-Process-Time containing the time in seconds that it
took to process the request and generate a response:

import time

from fastapi import FastAPI, Request

app = FastAPI()

@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time
response.headers["X-Process-Time"] = str(process_time)
return response

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Other middlewares
You can later read more about other middlewares in the Advanced User Guide: Advanced
Middleware ↪.

You will read about how to handle CORS with a middleware in the next section.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
CORS (Cross-Origin Resource Sharing)

CORS or "Cross-Origin Resource Sharing" [↪] refers to the situations when a frontend running in a
browser has JavaScript code that communicates with a backend, and the backend is in a different
"origin" than the frontend.

Origin
An origin is the combination of protocol ( http , https ), domain ( myapp.com , localhost ,
localhost.tiangolo.com ), and port ( 80 , 443 , 8080 ).

So, all these are different origins:

http://localhost

https://localhost

http://localhost:8080

Even if they are all in localhost , they use different protocols or ports, so, they are different
"origins".

Steps
So, let's say you have a frontend running in your browser at http://localhost:8080 , and its
JavaScript is trying to communicate with a backend running at http://localhost (because we
don't specify a port, the browser will assume the default port 80 ).

Then, the browser will send an HTTP OPTIONS request to the backend, and if the backend sends the
appropriate headers authorizing the communication from this different origin (
http://localhost:8080 ) then the browser will let the JavaScript in the frontend send its request to
the backend.

To achieve this, the backend must have a list of "allowed origins".

In this case, it would have to include http://localhost:8080 for the frontend to work correctly.

Wildcards

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
It's also possible to declare the list as "*" (a "wildcard") to say that all are allowed.

But that will only allow certain types of communication, excluding everything that involves
credentials: Cookies, Authorization headers like those used with Bearer Tokens, etc.

So, for everything to work correctly, it's better to specify explicitly the allowed origins.

Use CORSMiddleware
You can configure it in your FastAPI application using the CORSMiddleware .

Import CORSMiddleware .

Create a list of allowed origins (as strings).

Add it as a "middleware" to your FastAPI application.

You can also specify if your backend allows:

Credentials (Authorization headers, Cookies, etc).

Specific HTTP methods ( POST , PUT ) or all of them with the wildcard "*" .

Specific HTTP headers or all of them with the wildcard "*" .

from fastapi import FastAPI


from fastapi.middleware.cors import CORSMiddleware

app = FastAPI()

origins = [
"http://localhost.tiangolo.com",
"https://localhost.tiangolo.com",
"http://localhost",
"http://localhost:8080",
]

app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

@app.get("/")
async def main():
return {"message": "Hello World"}

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
The default parameters used by the CORSMiddleware implementation are restrictive by default, so
you'll need to explicitly enable particular origins, methods, or headers, in order for browsers to be
permitted to use them in a Cross-Domain context.

The following arguments are supported:

allow_origins - A list of origins that should be permitted to make cross-origin requests. E.g.
['https://example.org', 'https://www.example.org'] . You can use ['*'] to allow any
origin.

allow_origin_regex - A regex string to match against origins that should be permitted to


make cross-origin requests. e.g. 'https://.*\.example\.org' .

allow_methods - A list of HTTP methods that should be allowed for cross-origin requests.
Defaults to ['GET'] . You can use ['*'] to allow all standard methods.

allow_headers - A list of HTTP request headers that should be supported for cross-origin
requests. Defaults to [] . You can use ['*'] to allow all headers. The Accept ,
Accept-Language , Content-Language and Content-Type headers are always allowed for
simple CORS requests [↪].

allow_credentials - Indicate that cookies should be supported for cross-origin requests.


Defaults to False . Also, allow_origins cannot be set to ['*'] for credentials to be allowed,
origins must be specified.

expose_headers - Indicate any response headers that should be made accessible to the
browser. Defaults to [] .

max_age - Sets a maximum time in seconds for browsers to cache CORS responses. Defaults
to 600 .

The middleware responds to two particular types of HTTP request...

CORS preflight requests

These are any OPTIONS request with Origin and Access-Control-Request-Method headers.

In this case the middleware will intercept the incoming request and respond with appropriate CORS
headers, and either a 200 or 400 response for informational purposes.

Simple requests

Any request with an Origin header. In this case the middleware will pass the request through as
normal, but will include appropriate CORS headers on the response.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
More info
For more info about CORS, check the Mozilla CORS documentation [↪].

Technical Details

You could also use from starlette.middleware.cors import CORSMiddleware .

FastAPI provides several middlewares in fastapi.middleware just as a convenience for you, the developer. But most of
the available middlewares come directly from Starlette.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
SQL (Relational) Databases

Info

These docs are about to be updated. 🎉


The current version assumes Pydantic v1, and SQLAlchemy versions less than 2.0.

The new docs will include Pydantic v2 and will use SQLModel [↪] (which is also based on SQLAlchemy) once it is updated to use
Pydantic v2 as well.

FastAPI doesn't require you to use a SQL (relational) database.

But you can use any relational database that you want.

Here we'll see an example using SQLAlchemy [↪].

You can easily adapt it to any database supported by SQLAlchemy, like:

PostgreSQL

MySQL

SQLite

Oracle

Microsoft SQL Server, etc.

In this example, we'll use SQLite, because it uses a single file and Python has integrated support. So, you
can copy this example and run it as is.

Later, for your production application, you might want to use a database server like PostgreSQL.

Tip

There is an official project generator with FastAPI and PostgreSQL, all based on Docker, including a frontend and more tools:
https://github.com/tiangolo/full-stack-fastapi-postgresql [↪]

Note

Notice that most of the code is the standard SQLAlchemy code you would use with any framework.

The FastAPI specific code is as small as always.

ORMs

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
FastAPI works with any database and any style of library to talk to the database.

A common pattern is to use an "ORM": an "object-relational mapping" library.

An ORM has tools to convert ("map") between objects in code and database tables ("relations").

With an ORM, you normally create a class that represents a table in a SQL database, each attribute of the
class represents a column, with a name and a type.

For example a class Pet could represent a SQL table pets .

And each instance object of that class represents a row in the database.

For example an object orion_cat (an instance of Pet ) could have an attribute orion_cat.type , for the
column type . And the value of that attribute could be, e.g. "cat" .

These ORMs also have tools to make the connections or relations between tables or entities.

This way, you could also have an attribute orion_cat.owner and the owner would contain the data for this
pet's owner, taken from the table owners.

So, orion_cat.owner.name could be the name (from the name column in the owners table) of this pet's
owner.

It could have a value like "Arquilian" .

And the ORM will do all the work to get the information from the corresponding table owners when you try
to access it from your pet object.

Common ORMs are for example: Django-ORM (part of the Django framework), SQLAlchemy ORM (part of
SQLAlchemy, independent of framework) and Peewee (independent of framework), among others.

Here we will see how to work with SQLAlchemy ORM.

In a similar way you could use any other ORM.

Tip

There's an equivalent article using Peewee here in the docs.

File structure
For these examples, let's say you have a directory named my_super_project that contains a sub-directory
called sql_app with a structure like this:

.
└── sql_app

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
├── __init__.py
├── crud.py
├── database.py
├── main.py
├── models.py
└── schemas.py

The file __init__.py is just an empty file, but it tells Python that sql_app with all its modules (Python
files) is a package.

Now let's see what each file/module does.

Install SQLAlchemy
First you need to install SQLAlchemy :

bash

fast →
$ pip install sqlalchemy ▋

Create the SQLAlchemy parts


Let's refer to the file sql_app/database.py .

Import the SQLAlchemy parts

from sqlalchemy import create_engine


from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db"
# SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db"

engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Create a database URL for SQLAlchemy

from sqlalchemy import create_engine


from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db"
# SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db"

engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()

In this example, we are "connecting" to a SQLite database (opening a file with the SQLite database).

The file will be located at the same directory in the file sql_app.db .

That's why the last part is ./sql_app.db .

If you were using a PostgreSQL database instead, you would just have to uncomment the line:

SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db"

...and adapt it with your database data and credentials (equivalently for MySQL, MariaDB or any other).

Tip

This is the main line that you would have to modify if you wanted to use a different database.

Create the SQLAlchemy engine

The first step is to create a SQLAlchemy "engine".

We will later use this engine in other places.

from sqlalchemy import create_engine


from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db"
# SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db"

engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Base = declarative_base()

Note

The argument:

connect_args={"check_same_thread": False}

...is needed only for SQLite . It's not needed for other databases.

Technical Details

By default SQLite will only allow one thread to communicate with it, assuming that each thread would handle an independent
request.

This is to prevent accidentally sharing the same connection for different things (for different requests).

But in FastAPI, using normal functions ( def ) more than one thread could interact with the database for the same request, so we
need to make SQLite know that it should allow that with connect_args={"check_same_thread": False} .

Also, we will make sure each request gets its own database connection session in a dependency, so there's no need for that
default mechanism.

Create a SessionLocal class

Each instance of the SessionLocal class will be a database session. The class itself is not a database
session yet.

But once we create an instance of the SessionLocal class, this instance will be the actual database
session.

We name it SessionLocal to distinguish it from the Session we are importing from SQLAlchemy.

We will use Session (the one imported from SQLAlchemy) later.

To create the SessionLocal class, use the function sessionmaker :

from sqlalchemy import create_engine


from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db"
# SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db"

engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Create a Base class

Now we will use the function declarative_base() that returns a class.

Later we will inherit from this class to create each of the database models or classes (the ORM models):

from sqlalchemy import create_engine


from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db"
# SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db"

engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()

Create the database models


Let's now see the file sql_app/models.py .

Create SQLAlchemy models from the Base class

We will use this Base class we created before to create the SQLAlchemy models.

Tip

SQLAlchemy uses the term "model" to refer to these classes and instances that interact with the database.

But Pydantic also uses the term "model" to refer to something different, the data validation, conversion, and documentation
classes and instances.

Import Base from database (the file database.py from above).

Create classes that inherit from it.

These classes are the SQLAlchemy models.

from sqlalchemy import Boolean, Column, ForeignKey, Integer, String


from sqlalchemy.orm import relationship

from .database import Base

class User(Base):
__tablename__ = "users"

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
id = Column(Integer, primary_key=True)
email = Column(String, unique=True, index=True)
hashed_password = Column(String)
is_active = Column(Boolean, default=True)

items = relationship("Item", back_populates="owner")

class Item(Base):
__tablename__ = "items"

id = Column(Integer, primary_key=True)
title = Column(String, index=True)
description = Column(String, index=True)
owner_id = Column(Integer, ForeignKey("users.id"))

owner = relationship("User", back_populates="items")

The __tablename__ attribute tells SQLAlchemy the name of the table to use in the database for each of
these models.

Create model attributes/columns

Now create all the model (class) attributes.

Each of these attributes represents a column in its corresponding database table.

We use Column from SQLAlchemy as the default value.

And we pass a SQLAlchemy class "type", as Integer , String , and Boolean , that defines the type in the
database, as an argument.

from sqlalchemy import Boolean, Column, ForeignKey, Integer, String


from sqlalchemy.orm import relationship

from .database import Base

class User(Base):
__tablename__ = "users"

id = Column(Integer, primary_key=True)
email = Column(String, unique=True, index=True)
hashed_password = Column(String)
is_active = Column(Boolean, default=True)

items = relationship("Item", back_populates="owner")

class Item(Base):
__tablename__ = "items"

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
id = Column(Integer, primary_key=True)
title = Column(String, index=True)
description = Column(String, index=True)
owner_id = Column(Integer, ForeignKey("users.id"))

owner = relationship("User", back_populates="items")

Create the relationships

Now create the relationships.

For this, we use relationship provided by SQLAlchemy ORM.

This will become, more or less, a "magic" attribute that will contain the values from other tables related to
this one.

from sqlalchemy import Boolean, Column, ForeignKey, Integer, String


from sqlalchemy.orm import relationship

from .database import Base

class User(Base):
__tablename__ = "users"

id = Column(Integer, primary_key=True)
email = Column(String, unique=True, index=True)
hashed_password = Column(String)
is_active = Column(Boolean, default=True)

items = relationship("Item", back_populates="owner")

class Item(Base):
__tablename__ = "items"

id = Column(Integer, primary_key=True)
title = Column(String, index=True)
description = Column(String, index=True)
owner_id = Column(Integer, ForeignKey("users.id"))

owner = relationship("User", back_populates="items")

When accessing the attribute items in a User , as in my_user.items , it will have a list of Item
SQLAlchemy models (from the items table) that have a foreign key pointing to this record in the users
table.

When you access my_user.items , SQLAlchemy will actually go and fetch the items from the database in
the items table and populate them here.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
And when accessing the attribute owner in an Item , it will contain a User SQLAlchemy model from the
users table. It will use the owner_id attribute/column with its foreign key to know which record to get
from the users table.

Create the Pydantic models


Now let's check the file sql_app/schemas.py .

Tip

To avoid confusion between the SQLAlchemy models and the Pydantic models, we will have the file models.py with the
SQLAlchemy models, and the file schemas.py with the Pydantic models.

These Pydantic models define more or less a "schema" (a valid data shape).

So this will help us avoiding confusion while using both.

Create initial Pydantic models / schemas

Create an ItemBase and UserBase Pydantic models (or let's say "schemas") to have common attributes
while creating or reading data.

And create an ItemCreate and UserCreate that inherit from them (so they will have the same attributes),
plus any additional data (attributes) needed for creation.

So, the user will also have a password when creating it.

But for security, the password won't be in other Pydantic models, for example, it won't be sent from the API
when reading a user.

Python 3.10+ Python 3.9+ Python 3.8+

from pydantic import BaseModel

class ItemBase(BaseModel):
title: str
description: str | None = None

class ItemCreate(ItemBase):
pass

class Item(ItemBase):
id: int
owner_id: int

class Config:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
orm_mode = True

class UserBase(BaseModel):
email: str

class UserCreate(UserBase):
password: str

class User(UserBase):
id: int
is_active: bool
items: list[Item] = []

class Config:
orm_mode = True

SQLAlchemy style and Pydantic style

Notice that SQLAlchemy models define attributes using = , and pass the type as a parameter to Column ,
like in:

name = Column(String)

while Pydantic models declare the types using : , the new type annotation syntax/type hints:

name: str

Keep these in mind, so you don't get confused when using = and : with them.

Create Pydantic models / schemas for reading / returning

Now create Pydantic models (schemas) that will be used when reading data, when returning it from the
API.

For example, before creating an item, we don't know what will be the ID assigned to it, but when reading it
(when returning it from the API) we will already know its ID.

The same way, when reading a user, we can now declare that items will contain the items that belong to
this user.

Not only the IDs of those items, but all the data that we defined in the Pydantic model for reading items:
Item .

Python 3.10+ Python 3.9+ Python 3.8+

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
from pydantic import BaseModel

class ItemBase(BaseModel):
title: str
description: str | None = None

class ItemCreate(ItemBase):
pass

class Item(ItemBase):
id: int
owner_id: int

class Config:
orm_mode = True

class UserBase(BaseModel):
email: str

class UserCreate(UserBase):
password: str

class User(UserBase):
id: int
is_active: bool
items: list[Item] = []

class Config:
orm_mode = True

Tip

Notice that the User , the Pydantic model that will be used when reading a user (returning it from the API) doesn't include the
password .

Use Pydantic's orm_mode

Now, in the Pydantic models for reading, Item and User , add an internal Config class.

This Config [↪] class is used to provide configurations to Pydantic.

In the Config class, set the attribute orm_mode = True .

Python 3.10+ Python 3.9+ Python 3.8+

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
from pydantic import BaseModel

class ItemBase(BaseModel):
title: str
description: str | None = None

class ItemCreate(ItemBase):
pass

class Item(ItemBase):
id: int
owner_id: int

class Config:
orm_mode = True

class UserBase(BaseModel):
email: str

class UserCreate(UserBase):
password: str

class User(UserBase):
id: int
is_active: bool
items: list[Item] = []

class Config:
orm_mode = True

Tip

Notice it's assigning a value with = , like:

orm_mode = True

It doesn't use : as for the type declarations before.

This is setting a config value, not declaring a type.

Pydantic's orm_mode will tell the Pydantic model to read the data even if it is not a dict , but an ORM
model (or any other arbitrary object with attributes).

This way, instead of only trying to get the id value from a dict , as in:

id = data["id"]

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
it will also try to get it from an attribute, as in:

id = data.id

And with this, the Pydantic model is compatible with ORMs, and you can just declare it in the
response_model argument in your path operations.

You will be able to return a database model and it will read the data from it.

Technical Details about ORM mode

SQLAlchemy and many others are by default "lazy loading".

That means, for example, that they don't fetch the data for relationships from the database unless you try
to access the attribute that would contain that data.

For example, accessing the attribute items :

current_user.items

would make SQLAlchemy go to the items table and get the items for this user, but not before.

Without orm_mode , if you returned a SQLAlchemy model from your path operation, it wouldn't include the
relationship data.

Even if you declared those relationships in your Pydantic models.

But with ORM mode, as Pydantic itself will try to access the data it needs from attributes (instead of
assuming a dict ), you can declare the specific data you want to return and it will be able to go and get it,
even from ORMs.

CRUD utils
Now let's see the file sql_app/crud.py .

In this file we will have reusable functions to interact with the data in the database.

CRUD comes from: Create, Read, Update, and Delete.

...although in this example we are only creating and reading.

Read data

Import Session from sqlalchemy.orm , this will allow you to declare the type of the db parameters and
have better type checks and completion in your functions.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Import models (the SQLAlchemy models) and schemas (the Pydantic models / schemas).

Create utility functions to:

Read a single user by ID and by email.

Read multiple users.

Read multiple items.

from sqlalchemy.orm import Session

from . import models, schemas

def get_user(db: Session, user_id: int):


return db.query(models.User).filter(models.User.id == user_id).first()

def get_user_by_email(db: Session, email: str):


return db.query(models.User).filter(models.User.email == email).first()

def get_users(db: Session, skip: int = 0, limit: int = 100):


return db.query(models.User).offset(skip).limit(limit).all()

def create_user(db: Session, user: schemas.UserCreate):


fake_hashed_password = user.password + "notreallyhashed"
db_user = models.User(email=user.email, hashed_password=fake_hashed_password)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user

def get_items(db: Session, skip: int = 0, limit: int = 100):


return db.query(models.Item).offset(skip).limit(limit).all()

def create_user_item(db: Session, item: schemas.ItemCreate, user_id: int):


db_item = models.Item(**item.dict(), owner_id=user_id)
db.add(db_item)
db.commit()
db.refresh(db_item)
return db_item

Tip

By creating functions that are only dedicated to interacting with the database (get a user or an item) independent of your path
operation function, you can more easily reuse them in multiple parts and also add unit tests for them.

Create data

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Now create utility functions to create data.

The steps are:

Create a SQLAlchemy model instance with your data.

add that instance object to your database session.

commit the changes to the database (so that they are saved).

refresh your instance (so that it contains any new data from the database, like the generated ID).

from sqlalchemy.orm import Session

from . import models, schemas

def get_user(db: Session, user_id: int):


return db.query(models.User).filter(models.User.id == user_id).first()

def get_user_by_email(db: Session, email: str):


return db.query(models.User).filter(models.User.email == email).first()

def get_users(db: Session, skip: int = 0, limit: int = 100):


return db.query(models.User).offset(skip).limit(limit).all()

def create_user(db: Session, user: schemas.UserCreate):


fake_hashed_password = user.password + "notreallyhashed"
db_user = models.User(email=user.email, hashed_password=fake_hashed_password)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user

def get_items(db: Session, skip: int = 0, limit: int = 100):


return db.query(models.Item).offset(skip).limit(limit).all()

def create_user_item(db: Session, item: schemas.ItemCreate, user_id: int):


db_item = models.Item(**item.dict(), owner_id=user_id)
db.add(db_item)
db.commit()
db.refresh(db_item)
return db_item

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Info

In Pydantic v1 the method was called .dict() , it was deprecated (but still supported) in Pydantic v2, and renamed to
.model_dump() .

The examples here use .dict() for compatibility with Pydantic v1, but you should use .model_dump() instead if you can use
Pydantic v2.

Tip

The SQLAlchemy model for User contains a hashed_password that should contain a secure hashed version of the password.

But as what the API client provides is the original password, you need to extract it and generate the hashed password in your
application.

And then pass the hashed_password argument with the value to save.

Warning

This example is not secure, the password is not hashed.

In a real life application you would need to hash the password and never save them in plaintext.

For more details, go back to the Security section in the tutorial.

Here we are focusing only on the tools and mechanics of databases.

Tip

Instead of passing each of the keyword arguments to Item and reading each one of them from the Pydantic model, we are
generating a dict with the Pydantic model's data with:

item.dict()

and then we are passing the dict 's key-value pairs as the keyword arguments to the SQLAlchemy Item , with:

Item(**item.dict())

And then we pass the extra keyword argument owner_id that is not provided by the Pydantic model, with:

Item(**item.dict(), owner_id=user_id)

Main FastAPI app


And now in the file sql_app/main.py let's integrate and use all the other parts we created before.

Create the database tables

In a very simplistic way create the database tables:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Python 3.9+ Python 3.8+

from fastapi import Depends, FastAPI, HTTPException


from sqlalchemy.orm import Session

from . import crud, models, schemas


from .database import SessionLocal, engine

models.Base.metadata.create_all(bind=engine)

app = FastAPI()

# Dependency
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()

@app.post("/users/", response_model=schemas.User)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
db_user = crud.get_user_by_email(db, email=user.email)
if db_user:
raise HTTPException(status_code=400, detail="Email already registered")
return crud.create_user(db=db, user=user)

@app.get("/users/", response_model=list[schemas.User])
def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
users = crud.get_users(db, skip=skip, limit=limit)
return users

@app.get("/users/{user_id}", response_model=schemas.User)
def read_user(user_id: int, db: Session = Depends(get_db)):
db_user = crud.get_user(db, user_id=user_id)
if db_user is None:
raise HTTPException(status_code=404, detail="User not found")
return db_user

@app.post("/users/{user_id}/items/", response_model=schemas.Item)
def create_item_for_user(
user_id: int, item: schemas.ItemCreate, db: Session = Depends(get_db)
):
return crud.create_user_item(db=db, item=item, user_id=user_id)

@app.get("/items/", response_model=list[schemas.Item])
def read_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
items = crud.get_items(db, skip=skip, limit=limit)
return items

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Alembic Note

Normally you would probably initialize your database (create tables, etc) with Alembic [↪].

And you would also use Alembic for "migrations" (that's its main job).

A "migration" is the set of steps needed whenever you change the structure of your SQLAlchemy models,
add a new attribute, etc. to replicate those changes in the database, add a new column, a new table, etc.

You can find an example of Alembic in a FastAPI project in the templates from Project Generation -
Template ↪. Specifically in the alembic directory in the source code [↪].

Create a dependency

Now use the SessionLocal class we created in the sql_app/database.py file to create a dependency.

We need to have an independent database session/connection ( SessionLocal ) per request, use the same
session through all the request and then close it after the request is finished.

And then a new session will be created for the next request.

For that, we will create a new dependency with yield , as explained before in the section about
Dependencies with yield ↪.

Our dependency will create a new SQLAlchemy SessionLocal that will be used in a single request, and
then close it once the request is finished.

Python 3.9+ Python 3.8+

from fastapi import Depends, FastAPI, HTTPException


from sqlalchemy.orm import Session

from . import crud, models, schemas


from .database import SessionLocal, engine

models.Base.metadata.create_all(bind=engine)

app = FastAPI()

# Dependency
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()

@app.post("/users/", response_model=schemas.User)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
db_user = crud.get_user_by_email(db, email=user.email)
if db_user:
raise HTTPException(status_code=400, detail="Email already registered")
return crud.create_user(db=db, user=user)

@app.get("/users/", response_model=list[schemas.User])
def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
users = crud.get_users(db, skip=skip, limit=limit)
return users

@app.get("/users/{user_id}", response_model=schemas.User)
def read_user(user_id: int, db: Session = Depends(get_db)):
db_user = crud.get_user(db, user_id=user_id)
if db_user is None:
raise HTTPException(status_code=404, detail="User not found")
return db_user

@app.post("/users/{user_id}/items/", response_model=schemas.Item)
def create_item_for_user(
user_id: int, item: schemas.ItemCreate, db: Session = Depends(get_db)
):
return crud.create_user_item(db=db, item=item, user_id=user_id)

@app.get("/items/", response_model=list[schemas.Item])
def read_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
items = crud.get_items(db, skip=skip, limit=limit)
return items

Info

We put the creation of the SessionLocal() and handling of the requests in a try block.

And then we close it in the finally block.

This way we make sure the database session is always closed after the request. Even if there was an exception while processing
the request.

But you can't raise another exception from the exit code (after yield ). See more in Dependencies with yield and
HTTPException ↪

And then, when using the dependency in a path operation function, we declare it with the type Session we
imported directly from SQLAlchemy.

This will then give us better editor support inside the path operation function, because the editor will know
that the db parameter is of type Session :

Python 3.9+ Python 3.8+

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
from fastapi import Depends, FastAPI, HTTPException
from sqlalchemy.orm import Session

from . import crud, models, schemas


from .database import SessionLocal, engine

models.Base.metadata.create_all(bind=engine)

app = FastAPI()

# Dependency
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()

@app.post("/users/", response_model=schemas.User)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
db_user = crud.get_user_by_email(db, email=user.email)
if db_user:
raise HTTPException(status_code=400, detail="Email already registered")
return crud.create_user(db=db, user=user)

@app.get("/users/", response_model=list[schemas.User])
def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
users = crud.get_users(db, skip=skip, limit=limit)
return users

@app.get("/users/{user_id}", response_model=schemas.User)
def read_user(user_id: int, db: Session = Depends(get_db)):
db_user = crud.get_user(db, user_id=user_id)
if db_user is None:
raise HTTPException(status_code=404, detail="User not found")
return db_user

@app.post("/users/{user_id}/items/", response_model=schemas.Item)
def create_item_for_user(
user_id: int, item: schemas.ItemCreate, db: Session = Depends(get_db)
):
return crud.create_user_item(db=db, item=item, user_id=user_id)

@app.get("/items/", response_model=list[schemas.Item])
def read_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
items = crud.get_items(db, skip=skip, limit=limit)
return items

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Technical Details

The parameter db is actually of type SessionLocal , but this class (created with sessionmaker() ) is a "proxy" of a SQLAlchemy
Session , so, the editor doesn't really know what methods are provided.

But by declaring the type as Session , the editor now can know the available methods ( .add() , .query() , .commit() , etc) and
can provide better support (like completion). The type declaration doesn't affect the actual object.

Create your FastAPI path operations

Now, finally, here's the standard FastAPI path operations code.

Python 3.9+ Python 3.8+

from fastapi import Depends, FastAPI, HTTPException


from sqlalchemy.orm import Session

from . import crud, models, schemas


from .database import SessionLocal, engine

models.Base.metadata.create_all(bind=engine)

app = FastAPI()

# Dependency
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()

@app.post("/users/", response_model=schemas.User)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
db_user = crud.get_user_by_email(db, email=user.email)
if db_user:
raise HTTPException(status_code=400, detail="Email already registered")
return crud.create_user(db=db, user=user)

@app.get("/users/", response_model=list[schemas.User])
def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
users = crud.get_users(db, skip=skip, limit=limit)
return users

@app.get("/users/{user_id}", response_model=schemas.User)
def read_user(user_id: int, db: Session = Depends(get_db)):
db_user = crud.get_user(db, user_id=user_id)
if db_user is None:
raise HTTPException(status_code=404, detail="User not found")

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
return db_user

@app.post("/users/{user_id}/items/", response_model=schemas.Item)
def create_item_for_user(
user_id: int, item: schemas.ItemCreate, db: Session = Depends(get_db)
):
return crud.create_user_item(db=db, item=item, user_id=user_id)

@app.get("/items/", response_model=list[schemas.Item])
def read_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
items = crud.get_items(db, skip=skip, limit=limit)
return items

We are creating the database session before each request in the dependency with yield , and then closing
it afterwards.

And then we can create the required dependency in the path operation function, to get that session directly.

With that, we can just call crud.get_user directly from inside of the path operation function and use that
session.

Tip

Notice that the values you return are SQLAlchemy models, or lists of SQLAlchemy models.

But as all the path operations have a response_model with Pydantic models / schemas using orm_mode , the data declared in
your Pydantic models will be extracted from them and returned to the client, with all the normal filtering and validation.

Tip

Also notice that there are response_models that have standard Python types like List[schemas.Item] .

But as the content/parameter of that List is a Pydantic model with orm_mode , the data will be retrieved and returned to the
client as normally, without problems.

About def vs async def

Here we are using SQLAlchemy code inside of the path operation function and in the dependency, and, in
turn, it will go and communicate with an external database.

That could potentially require some "waiting".

But as SQLAlchemy doesn't have compatibility for using await directly, as would be with something like:

user = await db.query(User).first()

...and instead we are using:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
user = db.query(User).first()

Then we should declare the path operation functions and the dependency without async def , just with a
normal def , as:

@app.get("/users/{user_id}", response_model=schemas.User)
def read_user(user_id: int, db: Session = Depends(get_db)):
db_user = crud.get_user(db, user_id=user_id)
...

Info

If you need to connect to your relational database asynchronously, see Async SQL (Relational) Databases ↪.

Very Technical Details

If you are curious and have a deep technical knowledge, you can check the very technical details of how this async def vs def
is handled in the Async ↪ docs.

Migrations
Because we are using SQLAlchemy directly and we don't require any kind of plug-in for it to work with
FastAPI, we could integrate database migrations with Alembic [↪] directly.

And as the code related to SQLAlchemy and the SQLAlchemy models lives in separate independent files,
you would even be able to perform the migrations with Alembic without having to install FastAPI, Pydantic,
or anything else.

The same way, you would be able to use the same SQLAlchemy models and utilities in other parts of your
code that are not related to FastAPI.

For example, in a background task worker with Celery [↪], RQ [↪], or ARQ [↪].

Review all the files


Remember you should have a directory named my_super_project that contains a sub-directory called
sql_app .

sql_app should have the following files:

sql_app/__init__.py : is an empty file.

sql_app/database.py :

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

SQLALCHEMY_DATABASE_URL = "sqlite:///./sql_app.db"
# SQLALCHEMY_DATABASE_URL = "postgresql://user:password@postgresserver/db"

engine = create_engine(
SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False}
)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

Base = declarative_base()

sql_app/models.py :

from sqlalchemy import Boolean, Column, ForeignKey, Integer, String


from sqlalchemy.orm import relationship

from .database import Base

class User(Base):
__tablename__ = "users"

id = Column(Integer, primary_key=True)
email = Column(String, unique=True, index=True)
hashed_password = Column(String)
is_active = Column(Boolean, default=True)

items = relationship("Item", back_populates="owner")

class Item(Base):
__tablename__ = "items"

id = Column(Integer, primary_key=True)
title = Column(String, index=True)
description = Column(String, index=True)
owner_id = Column(Integer, ForeignKey("users.id"))

owner = relationship("User", back_populates="items")

sql_app/schemas.py :

Python 3.10+ Python 3.9+ Python 3.8+

from pydantic import BaseModel

class ItemBase(BaseModel):
title: str
description: str | None = None

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
class ItemCreate(ItemBase):
pass

class Item(ItemBase):
id: int
owner_id: int

class Config:
orm_mode = True

class UserBase(BaseModel):
email: str

class UserCreate(UserBase):
password: str

class User(UserBase):
id: int
is_active: bool
items: list[Item] = []

class Config:
orm_mode = True

sql_app/crud.py :

from sqlalchemy.orm import Session

from . import models, schemas

def get_user(db: Session, user_id: int):


return db.query(models.User).filter(models.User.id == user_id).first()

def get_user_by_email(db: Session, email: str):


return db.query(models.User).filter(models.User.email == email).first()

def get_users(db: Session, skip: int = 0, limit: int = 100):


return db.query(models.User).offset(skip).limit(limit).all()

def create_user(db: Session, user: schemas.UserCreate):


fake_hashed_password = user.password + "notreallyhashed"
db_user = models.User(email=user.email, hashed_password=fake_hashed_password)
db.add(db_user)
db.commit()
db.refresh(db_user)
return db_user

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
def get_items(db: Session, skip: int = 0, limit: int = 100):
return db.query(models.Item).offset(skip).limit(limit).all()

def create_user_item(db: Session, item: schemas.ItemCreate, user_id: int):


db_item = models.Item(**item.dict(), owner_id=user_id)
db.add(db_item)
db.commit()
db.refresh(db_item)
return db_item

sql_app/main.py :

Python 3.9+ Python 3.8+

from fastapi import Depends, FastAPI, HTTPException


from sqlalchemy.orm import Session

from . import crud, models, schemas


from .database import SessionLocal, engine

models.Base.metadata.create_all(bind=engine)

app = FastAPI()

# Dependency
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()

@app.post("/users/", response_model=schemas.User)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
db_user = crud.get_user_by_email(db, email=user.email)
if db_user:
raise HTTPException(status_code=400, detail="Email already registered")
return crud.create_user(db=db, user=user)

@app.get("/users/", response_model=list[schemas.User])
def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
users = crud.get_users(db, skip=skip, limit=limit)
return users

@app.get("/users/{user_id}", response_model=schemas.User)
def read_user(user_id: int, db: Session = Depends(get_db)):
db_user = crud.get_user(db, user_id=user_id)
if db_user is None:
raise HTTPException(status_code=404, detail="User not found")

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
return db_user

@app.post("/users/{user_id}/items/", response_model=schemas.Item)
def create_item_for_user(
user_id: int, item: schemas.ItemCreate, db: Session = Depends(get_db)
):
return crud.create_user_item(db=db, item=item, user_id=user_id)

@app.get("/items/", response_model=list[schemas.Item])
def read_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
items = crud.get_items(db, skip=skip, limit=limit)
return items

Check it
You can copy this code and use it as is.

Info

In fact, the code shown here is part of the tests. As most of the code in these docs.

Then you can run it with Uvicorn:

bash

And then, you can open your browser at http://127.0.0.1:8000/docs [↪].

And you will be able to interact with your FastAPI application, reading data from a real database:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Interact with the database directly
If you want to explore the SQLite database (file) directly, independently of FastAPI, to debug its contents,
add tables, columns, records, modify data, etc. you can use DB Browser for SQLite [↪].

It will look like this:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
You can also use an online SQLite browser like SQLite Viewer [↪] or ExtendsClass [↪].

Alternative DB session with middleware


If you can't use dependencies with yield -- for example, if you are not using Python 3.7 and can't install
the "backports" mentioned above for Python 3.6 -- you can set up the session in a "middleware" in a similar
way.

A "middleware" is basically a function that is always executed for each request, with some code executed
before, and some code executed after the endpoint function.

Create a middleware

The middleware we'll add (just a function) will create a new SQLAlchemy SessionLocal for each request,
add it to the request and then close it once the request is finished.

Python 3.9+ Python 3.8+

from fastapi import Depends, FastAPI, HTTPException, Request, Response


from sqlalchemy.orm import Session

from . import crud, models, schemas


from .database import SessionLocal, engine

models.Base.metadata.create_all(bind=engine)

app = FastAPI()

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
@app.middleware("http")
async def db_session_middleware(request: Request, call_next):
response = Response("Internal server error", status_code=500)
try:
request.state.db = SessionLocal()
response = await call_next(request)
finally:
request.state.db.close()
return response

# Dependency
def get_db(request: Request):
return request.state.db

@app.post("/users/", response_model=schemas.User)
def create_user(user: schemas.UserCreate, db: Session = Depends(get_db)):
db_user = crud.get_user_by_email(db, email=user.email)
if db_user:
raise HTTPException(status_code=400, detail="Email already registered")
return crud.create_user(db=db, user=user)

@app.get("/users/", response_model=list[schemas.User])
def read_users(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
users = crud.get_users(db, skip=skip, limit=limit)
return users

@app.get("/users/{user_id}", response_model=schemas.User)
def read_user(user_id: int, db: Session = Depends(get_db)):
db_user = crud.get_user(db, user_id=user_id)
if db_user is None:
raise HTTPException(status_code=404, detail="User not found")
return db_user

@app.post("/users/{user_id}/items/", response_model=schemas.Item)
def create_item_for_user(
user_id: int, item: schemas.ItemCreate, db: Session = Depends(get_db)
):
return crud.create_user_item(db=db, item=item, user_id=user_id)

@app.get("/items/", response_model=list[schemas.Item])
def read_items(skip: int = 0, limit: int = 100, db: Session = Depends(get_db)):
items = crud.get_items(db, skip=skip, limit=limit)
return items

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Info

We put the creation of the SessionLocal() and handling of the requests in a try block.

And then we close it in the finally block.

This way we make sure the database session is always closed after the request. Even if there was an exception while processing
the request.

About request.state

request.state is a property of each Request object. It is there to store arbitrary objects attached to the
request itself, like the database session in this case. You can read more about it in
Starlette's docs about Request state [↪].

For us in this case, it helps us ensure a single database session is used through all the request, and then
closed afterwards (in the middleware).

Dependencies with yield or middleware

Adding a middleware here is similar to what a dependency with yield does, with some differences:

It requires more code and is a bit more complex.

The middleware has to be an async function.

If there is code in it that has to "wait" for the network, it could "block" your application there and
degrade performance a bit.

Although it's probably not very problematic here with the way SQLAlchemy works.

But if you added more code to the middleware that had a lot of I/O waiting, it could then be
problematic.

A middleware is run for every request.

So, a connection will be created for every request.

Even when the path operation that handles that request didn't need the DB.

Tip

It's probably better to use dependencies with yield when they are enough for the use case.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Info

Dependencies with yield were added recently to FastAPI.

A previous version of this tutorial only had the examples with a middleware and there are probably several applications using the
middleware for database session management.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Bigger Applications - Multiple Files

If you are building an application or a web API, it's rarely the case that you can put everything on a single
file.

FastAPI provides a convenience tool to structure your application while keeping all the flexibility.

Info

If you come from Flask, this would be the equivalent of Flask's Blueprints.

An example file structure


Let's say you have a file structure like this:

.
├── app
│ ├── __init__.py
│ ├── main.py
│ ├── dependencies.py
│ └── routers
│ │ ├── __init__.py
│ │ ├── items.py
│ │ └── users.py
│ └── internal
│ ├── __init__.py
│ └── admin.py

Tip

There are several __init__.py files: one in each directory or subdirectory.

This is what allows importing code from one file into another.

For example, in app/main.py you could have a line like:

from app.routers import items

The app directory contains everything. And it has an empty file app/__init__.py , so it is a "Python
package" (a collection of "Python modules"): app .

It contains an app/main.py file. As it is inside a Python package (a directory with a file __init__.py ),
it is a "module" of that package: app.main .

There's also an app/dependencies.py file, just like app/main.py , it is a "module": app.dependencies .

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
There's a subdirectory app/routers/ with another file __init__.py , so it's a "Python subpackage":
app.routers .

The file app/routers/items.py is inside a package, app/routers/ , so, it's a submodule:


app.routers.items .

The same with app/routers/users.py , it's another submodule: app.routers.users .

There's also a subdirectory app/internal/ with another file __init__.py , so it's another "Python
subpackage": app.internal .

And the file app/internal/admin.py is another submodule: app.internal.admin .

Package app
app/__init__.py

Module app.main
app/main.py Subpackage app.routers Subpackage app.internal
app/routers/__init__.py app/internal/__init__.py

Module app.dependencies Module app.routers.items Module app.internal.admin


app/dependencies.py app/routers/items.py app/internal/admin.py

Module app.routers.users
app/routers/users.py

The same file structure with comments:

.
├── app # "app" is a Python package
│ ├── __init__.py # this file makes "app" a "Python package"
│ ├── main.py # "main" module, e.g. import app.main
│ ├── dependencies.py # "dependencies" module, e.g. import app.dependencies
│ └── routers # "routers" is a "Python subpackage"
│ │ ├── __init__.py # makes "routers" a "Python subpackage"
│ │ ├── items.py # "items" submodule, e.g. import app.routers.items
│ │ └── users.py # "users" submodule, e.g. import app.routers.users
│ └── internal # "internal" is a "Python subpackage"
│ ├── __init__.py # makes "internal" a "Python subpackage"
│ └── admin.py # "admin" submodule, e.g. import app.internal.admin

APIRouter
Let's say the file dedicated to handling just users is the submodule at /app/routers/users.py .

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
You want to have the path operations related to your users separated from the rest of the code, to keep it
organized.

But it's still part of the same FastAPI application/web API (it's part of the same "Python Package").

You can create the path operations for that module using APIRouter .

Import APIRouter

You import it and create an "instance" the same way you would with the class FastAPI :

app/routers/users.py

from fastapi import APIRouter

router = APIRouter()

@router.get("/users/", tags=["users"])
async def read_users():
return [{"username": "Rick"}, {"username": "Morty"}]

@router.get("/users/me", tags=["users"])
async def read_user_me():
return {"username": "fakecurrentuser"}

@router.get("/users/{username}", tags=["users"])
async def read_user(username: str):
return {"username": username}

Path operations with APIRouter

And then you use it to declare your path operations.

Use it the same way you would use the FastAPI class:

app/routers/users.py

from fastapi import APIRouter

router = APIRouter()

@router.get("/users/", tags=["users"])
async def read_users():
return [{"username": "Rick"}, {"username": "Morty"}]

@router.get("/users/me", tags=["users"])

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
async def read_user_me():
return {"username": "fakecurrentuser"}

@router.get("/users/{username}", tags=["users"])
async def read_user(username: str):
return {"username": username}

You can think of APIRouter as a "mini FastAPI " class.

All the same options are supported.

All the same parameters , responses , dependencies , tags , etc.

Tip

In this example, the variable is called router , but you can name it however you want.

We are going to include this APIRouter in the main FastAPI app, but first, let's check the dependencies
and another APIRouter .

Dependencies
We see that we are going to need some dependencies used in several places of the application.

So we put them in their own dependencies module ( app/dependencies.py ).

We will now use a simple dependency to read a custom X-Token header:

Python 3.9+ Python 3.8+ Python 3.8+ non-Annotated

app/dependencies.py

from typing import Annotated

from fastapi import Header, HTTPException

async def get_token_header(x_token: Annotated[str, Header()]):


if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")

async def get_query_token(token: str):


if token != "jessica":
raise HTTPException(status_code=400, detail="No Jessica token provided")

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Tip

We are using an invented header to simplify this example.

But in real cases you will get better results using the integrated Security utilities ↪.

Another module with APIRouter


Let's say you also have the endpoints dedicated to handling "items" from your application in the module at
app/routers/items.py .

You have path operations for:

/items/

/items/{item_id}

It's all the same structure as with app/routers/users.py .

But we want to be smarter and simplify the code a bit.

We know all the path operations in this module have the same:

Path prefix : /items .

tags : (just one tag: items ).

Extra responses .

dependencies : they all need that X-Token dependency we created.

So, instead of adding all that to each path operation, we can add it to the APIRouter .

app/routers/items.py

from fastapi import APIRouter, Depends, HTTPException

from ..dependencies import get_token_header

router = APIRouter(
prefix="/items",
tags=["items"],
dependencies=[Depends(get_token_header)],
responses={404: {"description": "Not found"}},
)

fake_items_db = {"plumbus": {"name": "Plumbus"}, "gun": {"name": "Portal Gun"}}

@router.get("/")

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
async def read_items():
return fake_items_db

@router.get("/{item_id}")
async def read_item(item_id: str):
if item_id not in fake_items_db:
raise HTTPException(status_code=404, detail="Item not found")
return {"name": fake_items_db[item_id]["name"], "item_id": item_id}

@router.put(
"/{item_id}",
tags=["custom"],
responses={403: {"description": "Operation forbidden"}},
)
async def update_item(item_id: str):
if item_id != "plumbus":
raise HTTPException(
status_code=403, detail="You can only update the item: plumbus"
)
return {"item_id": item_id, "name": "The great Plumbus"}

As the path of each path operation has to start with / , like in:

@router.get("/{item_id}")
async def read_item(item_id: str):
...

...the prefix must not include a final / .

So, the prefix in this case is /items .

We can also add a list of tags and extra responses that will be applied to all the path operations included
in this router.

And we can add a list of dependencies that will be added to all the path operations in the router and will be
executed/solved for each request made to them.

Tip

Note that, much like dependencies in path operation decorators ↪, no value will be passed to your path operation function.

The end result is that the item paths are now:

/items/

/items/{item_id}

...as we intended.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
They will be marked with a list of tags that contain a single string "items" .

These "tags" are especially useful for the automatic interactive documentation systems (using
OpenAPI).

All of them will include the predefined responses .

All these path operations will have the list of dependencies evaluated/executed before them.

If you also declare dependencies in a specific path operation, they will be executed too.

The router dependencies are executed first, then the dependencies in the decorator ↪, and then
the normal parameter dependencies.

You can also add Security dependencies with scopes ↪.

Tip

Having dependencies in the APIRouter can be used, for example, to require authentication for a whole group of path operations.
Even if the dependencies are not added individually to each one of them.

Check

The prefix , tags , responses , and dependencies parameters are (as in many other cases) just a feature from FastAPI to help
you avoid code duplication.

Import the dependencies

This code lives in the module app.routers.items , the file app/routers/items.py .

And we need to get the dependency function from the module app.dependencies , the file
app/dependencies.py .

So we use a relative import with .. for the dependencies:

app/routers/items.py

from fastapi import APIRouter, Depends, HTTPException

from ..dependencies import get_token_header

router = APIRouter(
prefix="/items",
tags=["items"],
dependencies=[Depends(get_token_header)],
responses={404: {"description": "Not found"}},
)

fake_items_db = {"plumbus": {"name": "Plumbus"}, "gun": {"name": "Portal Gun"}}

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
@router.get("/")
async def read_items():
return fake_items_db

@router.get("/{item_id}")
async def read_item(item_id: str):
if item_id not in fake_items_db:
raise HTTPException(status_code=404, detail="Item not found")
return {"name": fake_items_db[item_id]["name"], "item_id": item_id}

@router.put(
"/{item_id}",
tags=["custom"],
responses={403: {"description": "Operation forbidden"}},
)
async def update_item(item_id: str):
if item_id != "plumbus":
raise HTTPException(
status_code=403, detail="You can only update the item: plumbus"
)
return {"item_id": item_id, "name": "The great Plumbus"}

How relative imports work

Tip

If you know perfectly how imports work, continue to the next section below.

A single dot . , like in:

from .dependencies import get_token_header

would mean:

Starting in the same package that this module (the file app/routers/items.py ) lives in (the directory
app/routers/ )...

find the module dependencies (an imaginary file at app/routers/dependencies.py )...

and from it, import the function get_token_header .

But that file doesn't exist, our dependencies are in a file at app/dependencies.py .

Remember how our app/file structure looks like:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Package app
app/__init__.py

Module app.main
app/main.py Subpackage app.routers Subpackage app.internal
app/routers/__init__.py app/internal/__init__.py

Module app.dependencies Module app.routers.items Module app.internal.admin


app/dependencies.py app/routers/items.py app/internal/admin.py

Module app.routers.users
app/routers/users.py

The two dots .. , like in:

from ..dependencies import get_token_header

mean:

Starting in the same package that this module (the file app/routers/items.py ) lives in (the directory
app/routers/ )...

go to the parent package (the directory app/ )...

and in there, find the module dependencies (the file at app/dependencies.py )...

and from it, import the function get_token_header .

That works correctly! 🎉


The same way, if we had used three dots ... , like in:

from ...dependencies import get_token_header

that would mean:

Starting in the same package that this module (the file app/routers/items.py ) lives in (the directory
app/routers/ )...

go to the parent package (the directory app/ )...

then go to the parent of that package (there's no parent package, app is the top level 😱)...
and in there, find the module dependencies (the file at app/dependencies.py )...

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
and from it, import the function get_token_header .

That would refer to some package above app/ , with its own file __init__.py , etc. But we don't have that.
So, that would throw an error in our example. 🚨
But now you know how it works, so you can use relative imports in your own apps no matter how complex
they are. 🤓
Add some custom tags , responses , and dependencies

We are not adding the prefix /items nor the tags=["items"] to each path operation because we added
them to the APIRouter .

But we can still add more tags that will be applied to a specific path operation, and also some extra
responses specific to that path operation:

app/routers/items.py

from fastapi import APIRouter, Depends, HTTPException

from ..dependencies import get_token_header

router = APIRouter(
prefix="/items",
tags=["items"],
dependencies=[Depends(get_token_header)],
responses={404: {"description": "Not found"}},
)

fake_items_db = {"plumbus": {"name": "Plumbus"}, "gun": {"name": "Portal Gun"}}

@router.get("/")
async def read_items():
return fake_items_db

@router.get("/{item_id}")
async def read_item(item_id: str):
if item_id not in fake_items_db:
raise HTTPException(status_code=404, detail="Item not found")
return {"name": fake_items_db[item_id]["name"], "item_id": item_id}

@router.put(
"/{item_id}",
tags=["custom"],
responses={403: {"description": "Operation forbidden"}},
)
async def update_item(item_id: str):
if item_id != "plumbus":

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
raise HTTPException(
status_code=403, detail="You can only update the item: plumbus"
)
return {"item_id": item_id, "name": "The great Plumbus"}

Tip

This last path operation will have the combination of tags: ["items", "custom"] .

And it will also have both responses in the documentation, one for 404 and one for 403 .

The main FastAPI


Now, let's see the module at app/main.py .

Here's where you import and use the class FastAPI .

This will be the main file in your application that ties everything together.

And as most of your logic will now live in its own specific module, the main file will be quite simple.

Import FastAPI

You import and create a FastAPI class as normally.

And we can even declare global dependencies ↪ that will be combined with the dependencies for each
APIRouter :

app/main.py

from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header


from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])

app.include_router(users.router)
app.include_router(items.router)
app.include_router(
admin.router,
prefix="/admin",
tags=["admin"],
dependencies=[Depends(get_token_header)],
responses={418: {"description": "I'm a teapot"}},
)

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
@app.get("/")
async def root():
return {"message": "Hello Bigger Applications!"}

Import the APIRouter

Now we import the other submodules that have APIRouter s:

app/main.py

from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header


from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])

app.include_router(users.router)
app.include_router(items.router)
app.include_router(
admin.router,
prefix="/admin",
tags=["admin"],
dependencies=[Depends(get_token_header)],
responses={418: {"description": "I'm a teapot"}},
)

@app.get("/")
async def root():
return {"message": "Hello Bigger Applications!"}

As the files app/routers/users.py and app/routers/items.py are submodules that are part of the same
Python package app , we can use a single dot . to import them using "relative imports".

How the importing works

The section:

from .routers import items, users

means:

Starting in the same package that this module (the file app/main.py ) lives in (the directory app/ )...

look for the subpackage routers (the directory at app/routers/ )...

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
and from it, import the submodule items (the file at app/routers/items.py ) and users (the file at
app/routers/users.py )...

The module items will have a variable router ( items.router ). This is the same one we created in the
file app/routers/items.py , it's an APIRouter object.

And then we do the same for the module users .

We could also import them like:

from app.routers import items, users

Info

The first version is a "relative import":

from .routers import items, users

The second version is an "absolute import":

from app.routers import items, users

To learn more about Python Packages and Modules, read the official Python documentation about Modules [↪].

Avoid name collisions

We are importing the submodule items directly, instead of importing just its variable router .

This is because we also have another variable named router in the submodule users .

If we had imported one after the other, like:

from .routers.items import router


from .routers.users import router

the router from users would overwrite the one from items and we wouldn't be able to use them at the
same time.

So, to be able to use both of them in the same file, we import the submodules directly:

app/main.py

from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header


from .internal import admin
from .routers import items, users

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
app = FastAPI(dependencies=[Depends(get_query_token)])

app.include_router(users.router)
app.include_router(items.router)
app.include_router(
admin.router,
prefix="/admin",
tags=["admin"],
dependencies=[Depends(get_token_header)],
responses={418: {"description": "I'm a teapot"}},
)

@app.get("/")
async def root():
return {"message": "Hello Bigger Applications!"}

Include the APIRouter s for users and items

Now, let's include the router s from the submodules users and items :

app/main.py

from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header


from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])

app.include_router(users.router)
app.include_router(items.router)
app.include_router(
admin.router,
prefix="/admin",
tags=["admin"],
dependencies=[Depends(get_token_header)],
responses={418: {"description": "I'm a teapot"}},
)

@app.get("/")
async def root():
return {"message": "Hello Bigger Applications!"}

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Info

users.router contains the APIRouter inside of the file app/routers/users.py .

And items.router contains the APIRouter inside of the file app/routers/items.py .

With app.include_router() we can add each APIRouter to the main FastAPI application.

It will include all the routes from that router as part of it.

Technical Details

It will actually internally create a path operation for each path operation that was declared in the APIRouter .

So, behind the scenes, it will actually work as if everything was the same single app.

Check

You don't have to worry about performance when including routers.

This will take microseconds and will only happen at startup.

So it won't affect performance. ⚡

Include an APIRouter with a custom prefix , tags , responses , and dependencies

Now, let's imagine your organization gave you the app/internal/admin.py file.

It contains an APIRouter with some admin path operations that your organization shares between several
projects.

For this example it will be super simple. But let's say that because it is shared with other projects in the
organization, we cannot modify it and add a prefix , dependencies , tags , etc. directly to the APIRouter :

app/internal/admin.py

from fastapi import APIRouter

router = APIRouter()

@router.post("/")
async def update_admin():
return {"message": "Admin getting schwifty"}

But we still want to set a custom prefix when including the APIRouter so that all its path operations
start with /admin , we want to secure it with the dependencies we already have for this project, and we

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
want to include tags and responses .

We can declare all that without having to modify the original APIRouter by passing those parameters to
app.include_router() :

app/main.py

from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header


from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])

app.include_router(users.router)
app.include_router(items.router)
app.include_router(
admin.router,
prefix="/admin",
tags=["admin"],
dependencies=[Depends(get_token_header)],
responses={418: {"description": "I'm a teapot"}},
)

@app.get("/")
async def root():
return {"message": "Hello Bigger Applications!"}

That way, the original APIRouter will keep unmodified, so we can still share that same
app/internal/admin.py file with other projects in the organization.

The result is that in our app, each of the path operations from the admin module will have:

The prefix /admin .

The tag admin .

The dependency get_token_header .

The response 418 . 🍵


But that will only affect that APIRouter in our app, not in any other code that uses it.

So, for example, other projects could use the same APIRouter with a different authentication method.

Include a path operation

We can also add path operations directly to the FastAPI app.

Here we do it... just to show that we can 🤷:


Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
app/main.py

from fastapi import Depends, FastAPI

from .dependencies import get_query_token, get_token_header


from .internal import admin
from .routers import items, users

app = FastAPI(dependencies=[Depends(get_query_token)])

app.include_router(users.router)
app.include_router(items.router)
app.include_router(
admin.router,
prefix="/admin",
tags=["admin"],
dependencies=[Depends(get_token_header)],
responses={418: {"description": "I'm a teapot"}},
)

@app.get("/")
async def root():
return {"message": "Hello Bigger Applications!"}

and it will work correctly, together with all the other path operations added with app.include_router() .

Very Technical Details

Note: this is a very technical detail that you probably can just skip.

The APIRouter s are not "mounted", they are not isolated from the rest of the application.

This is because we want to include their path operations in the OpenAPI schema and the user interfaces.

As we cannot just isolate them and "mount" them independently of the rest, the path operations are "cloned" (re-created), not
included directly.

Check the automatic API docs


Now, run uvicorn , using the module app.main and the variable app :

bash

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
And open the docs at http://127.0.0.1:8000/docs [↪].

You will see the automatic API docs, including the paths from all the submodules, using the correct paths
(and prefixes) and the correct tags:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Include the same router multiple times with different prefix
You can also use .include_router() multiple times with the same router using different prefixes.

This could be useful, for example, to expose the same API under different prefixes, e.g. /api/v1 and
/api/latest .

This is an advanced usage that you might not really need, but it's there in case you do.

Include an APIRouter in another


The same way you can include an APIRouter in a FastAPI application, you can include an APIRouter in
another APIRouter using:

router.include_router(other_router)

Make sure you do it before including router in the FastAPI app, so that the path operations from
other_router are also included.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Background Tasks

You can define background tasks to be run after returning a response.

This is useful for operations that need to happen after a request, but that the client doesn't really have to be
waiting for the operation to complete before receiving the response.

This includes, for example:

Email notifications sent after performing an action:

As connecting to an email server and sending an email tends to be "slow" (several seconds), you
can return the response right away and send the email notification in the background.

Processing data:

For example, let's say you receive a file that must go through a slow process, you can return a
response of "Accepted" (HTTP 202) and process it in the background.

Using BackgroundTasks
First, import BackgroundTasks and define a parameter in your path operation function with a type
declaration of BackgroundTasks :

from fastapi import BackgroundTasks, FastAPI

app = FastAPI()

def write_notification(email: str, message=""):


with open("log.txt", mode="w") as email_file:
content = f"notification for {email}: {message}"
email_file.write(content)

@app.post("/send-notification/{email}")
async def send_notification(email: str, background_tasks: BackgroundTasks):
background_tasks.add_task(write_notification, email, message="some notification")
return {"message": "Notification sent in the background"}

FastAPI will create the object of type BackgroundTasks for you and pass it as that parameter.

Create a task function


Create a function to be run as the background task.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
It is just a standard function that can receive parameters.

It can be an async def or normal def function, FastAPI will know how to handle it correctly.

In this case, the task function will write to a file (simulating sending an email).

And as the write operation doesn't use async and await , we define the function with normal def :

from fastapi import BackgroundTasks, FastAPI

app = FastAPI()

def write_notification(email: str, message=""):


with open("log.txt", mode="w") as email_file:
content = f"notification for {email}: {message}"
email_file.write(content)

@app.post("/send-notification/{email}")
async def send_notification(email: str, background_tasks: BackgroundTasks):
background_tasks.add_task(write_notification, email, message="some notification")
return {"message": "Notification sent in the background"}

Add the background task


Inside of your path operation function, pass your task function to the background tasks object with the
method .add_task() :

from fastapi import BackgroundTasks, FastAPI

app = FastAPI()

def write_notification(email: str, message=""):


with open("log.txt", mode="w") as email_file:
content = f"notification for {email}: {message}"
email_file.write(content)

@app.post("/send-notification/{email}")
async def send_notification(email: str, background_tasks: BackgroundTasks):
background_tasks.add_task(write_notification, email, message="some notification")
return {"message": "Notification sent in the background"}

.add_task() receives as arguments:

A task function to be run in the background ( write_notification ).

Any sequence of arguments that should be passed to the task function in order ( email ).

Any keyword arguments that should be passed to the task function ( message="some notification" ).

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Dependency Injection
Using BackgroundTasks also works with the dependency injection system, you can declare a parameter of
type BackgroundTasks at multiple levels: in a path operation function, in a dependency (dependable), in a
sub-dependency, etc.

FastAPI knows what to do in each case and how to reuse the same object, so that all the background tasks
are merged together and are run in the background afterwards:

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import BackgroundTasks, Depends, FastAPI

app = FastAPI()

def write_log(message: str):


with open("log.txt", mode="a") as log:
log.write(message)

def get_query(background_tasks: BackgroundTasks, q: str | None = None):


if q:
message = f"found query: {q}\n"
background_tasks.add_task(write_log, message)
return q

@app.post("/send-notification/{email}")
async def send_notification(
email: str, background_tasks: BackgroundTasks, q: Annotated[str, Depends(get_query)]
):
message = f"message to {email}\n"
background_tasks.add_task(write_log, message)
return {"message": "Message sent"}

In this example, the messages will be written to the log.txt file after the response is sent.

If there was a query in the request, it will be written to the log in a background task.

And then another background task generated at the path operation function will write a message using the
email path parameter.

Technical Details
The class BackgroundTasks comes directly from starlette.background [↪].

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
It is imported/included directly into FastAPI so that you can import it from fastapi and avoid accidentally
importing the alternative BackgroundTask (without the s at the end) from starlette.background .

By only using BackgroundTasks (and not BackgroundTask ), it's then possible to use it as a path operation
function parameter and have FastAPI handle the rest for you, just like when using the Request object
directly.

It's still possible to use BackgroundTask alone in FastAPI, but you have to create the object in your code
and return a Starlette Response including it.

You can see more details in Starlette's official docs for Background Tasks [↪].

Caveat
If you need to perform heavy background computation and you don't necessarily need it to be run by the
same process (for example, you don't need to share memory, variables, etc), you might benefit from using
other bigger tools like Celery [↪].

They tend to require more complex configurations, a message/job queue manager, like RabbitMQ or Redis,
but they allow you to run background tasks in multiple processes, and especially, in multiple servers.

To see an example, check the Project Generators ↪, they all include Celery already configured.

But if you need to access variables and objects from the same FastAPI app, or you need to perform small
background tasks (like sending an email notification), you can simply just use BackgroundTasks .

Recap
Import and use BackgroundTasks with parameters in path operation functions and dependencies to add
background tasks.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Metadata and Docs URLs

You can customize several metadata configurations in your FastAPI application.

Metadata for API


You can set the following fields that are used in the OpenAPI specification and the automatic API
docs UIs:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Parameter Type Description

title str The title of the API.

summary str A short summary of the API. Available since OpenAPI 3.1.0, FastAPI 0.99.0.

descriptio str A short description of the API. It can use Markdown.


n

version string The version of the API. This is the version of your own application, not of OpenAPI.
For example 2.5.0 .

terms_of_s str A URL to the Terms of Service for the API. If provided, this has to be a URL.
ervice

contact dict The contact information for the exposed API. It can contain several fields.

contact fields

Parameter Type Description

name str The identifying name of the contact


person/organization.

url str The URL pointing to the contact


information. MUST be in the format
of a URL.

email str The email address of the contact


person/organization. MUST be in the
format of an email address.

license_in dict The license information for the exposed API. It can contain several fields.
fo
license_info fields

Parameter Type Description

name str REQUIRED (if a license_info is


set). The license name used for the
API.

identifie str An SPDX [↪] license expression for


r the API. The identifier field is
mutually exclusive of the url field.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Available since OpenAPI 3.1.0, FastAPI 0.99.0.

url str A URL to the license used for the


API. MUST be in the format of a URL.

You can set them as follows:

from fastapi import FastAPI

description = """
ChimichangApp API helps you do awesome stuff. 🚀
## Items

You can **read items**.

## Users

You will be able to:

* **Create users** (_not implemented_).


* **Read users** (_not implemented_).
"""

app = FastAPI(
title="ChimichangApp",
description=description,
summary="Deadpool's favorite app. Nuff said.",
version="0.0.1",
terms_of_service="http://example.com/terms/",
contact={
"name": "Deadpoolio the Amazing",
"url": "http://x-force.example.com/contact/",
"email": "[email protected]",
},
license_info={
"name": "Apache 2.0",
"url": "https://www.apache.org/licenses/LICENSE-2.0.html",
},
)

@app.get("/items/")
async def read_items():
return [{"name": "Katana"}]

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Tip

You can write Markdown in the description field and it will be rendered in the output.

With this configuration, the automatic API docs would look like:

License identifier
Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Since OpenAPI 3.1.0 and FastAPI 0.99.0, you can also set the license_info with an identifier
instead of a url .

For example:

from fastapi import FastAPI

description = """
ChimichangApp API helps you do awesome stuff. 🚀
## Items

You can **read items**.

## Users

You will be able to:

* **Create users** (_not implemented_).


* **Read users** (_not implemented_).
"""

app = FastAPI(
title="ChimichangApp",
description=description,
summary="Deadpool's favorite app. Nuff said.",
version="0.0.1",
terms_of_service="http://example.com/terms/",
contact={
"name": "Deadpoolio the Amazing",
"url": "http://x-force.example.com/contact/",
"email": "[email protected]",
},
license_info={
"name": "Apache 2.0",
"identifier": "MIT",
},
)

@app.get("/items/")
async def read_items():
return [{"name": "Katana"}]

Metadata for tags


You can also add additional metadata for the different tags used to group your path operations with
the parameter openapi_tags .

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
It takes a list containing one dictionary for each tag.

Each dictionary can contain:

name (required): a str with the same tag name you use in the tags parameter in your path
operations and APIRouter s.

description : a str with a short description for the tag. It can have Markdown and will be
shown in the docs UI.

externalDocs : a dict describing external documentation with:

description : a str with a short description for the external docs.

url (required): a str with the URL for the external documentation.

Create metadata for tags

Let's try that in an example with tags for users and items .

Create metadata for your tags and pass it to the openapi_tags parameter:

from fastapi import FastAPI

tags_metadata = [
{
"name": "users",
"description": "Operations with users. The **login** logic is also here.",
},
{
"name": "items",
"description": "Manage items. So _fancy_ they have their own docs.",
"externalDocs": {
"description": "Items external docs",
"url": "https://fastapi.tiangolo.com/",
},
},
]

app = FastAPI(openapi_tags=tags_metadata)

@app.get("/users/", tags=["users"])
async def get_users():
return [{"name": "Harry"}, {"name": "Ron"}]

@app.get("/items/", tags=["items"])
async def get_items():
return [{"name": "wand"}, {"name": "flying broom"}]

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Notice that you can use Markdown inside of the descriptions, for example "login" will be shown in
bold (login) and "fancy" will be shown in italics (fancy).

Tip

You don't have to add metadata for all the tags that you use.

Use your tags

Use the tags parameter with your path operations (and APIRouter s) to assign them to different
tags:

from fastapi import FastAPI

tags_metadata = [
{
"name": "users",
"description": "Operations with users. The **login** logic is also here.",
},
{
"name": "items",
"description": "Manage items. So _fancy_ they have their own docs.",
"externalDocs": {
"description": "Items external docs",
"url": "https://fastapi.tiangolo.com/",
},
},
]

app = FastAPI(openapi_tags=tags_metadata)

@app.get("/users/", tags=["users"])
async def get_users():
return [{"name": "Harry"}, {"name": "Ron"}]

@app.get("/items/", tags=["items"])
async def get_items():
return [{"name": "wand"}, {"name": "flying broom"}]

Info

Read more about tags in Path Operation Configuration ↪.

Check the docs


Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Now, if you check the docs, they will show all the additional metadata:

Order of tags

The order of each tag metadata dictionary also defines the order shown in the docs UI.

For example, even though users would go after items in alphabetical order, it is shown before
them, because we added their metadata as the first dictionary in the list.

OpenAPI URL
By default, the OpenAPI schema is served at /openapi.json .

But you can configure it with the parameter openapi_url .

For example, to set it to be served at /api/v1/openapi.json :

from fastapi import FastAPI

app = FastAPI(openapi_url="/api/v1/openapi.json")

@app.get("/items/")

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
async def read_items():
return [{"name": "Foo"}]

If you want to disable the OpenAPI schema completely you can set openapi_url=None , that will
also disable the documentation user interfaces that use it.

Docs URLs
You can configure the two documentation user interfaces included:

Swagger UI: served at /docs .

You can set its URL with the parameter docs_url .

You can disable it by setting docs_url=None .

ReDoc: served at /redoc .

You can set its URL with the parameter redoc_url .

You can disable it by setting redoc_url=None .

For example, to set Swagger UI to be served at /documentation and disable ReDoc:

from fastapi import FastAPI

app = FastAPI(docs_url="/documentation", redoc_url=None)

@app.get("/items/")
async def read_items():
return [{"name": "Foo"}]

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Static Files

You can serve static files automatically from a directory using StaticFiles .

Use StaticFiles
Import StaticFiles .

"Mount" a StaticFiles() instance in a specific path.

from fastapi import FastAPI


from fastapi.staticfiles import StaticFiles

app = FastAPI()

app.mount("/static", StaticFiles(directory="static"), name="static")

Technical Details

You could also use from starlette.staticfiles import StaticFiles .

FastAPI provides the same starlette.staticfiles as fastapi.staticfiles just as a convenience for you, the
developer. But it actually comes directly from Starlette.

What is "Mounting"

"Mounting" means adding a complete "independent" application in a specific path, that then takes
care of handling all the sub-paths.

This is different from using an APIRouter as a mounted application is completely independent. The
OpenAPI and docs from your main application won't include anything from the mounted application,
etc.

You can read more about this in the Advanced User Guide ↪.

Details
The first "/static" refers to the sub-path this "sub-application" will be "mounted" on. So, any path
that starts with "/static" will be handled by it.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
The directory="static" refers to the name of the directory that contains your static files.

The name="static" gives it a name that can be used internally by FastAPI.

All these parameters can be different than " static ", adjust them with the needs and specific details
of your own application.

More info
For more details and options check Starlette's docs about Static Files [↪].

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Testing

Thanks to Starlette [↪], testing FastAPI applications is easy and enjoyable.

It is based on HTTPX [↪], which in turn is designed based on Requests, so it's very familiar and intuitive.

With it, you can use pytest [↪] directly with FastAPI.

Using TestClient

Info

To use TestClient , first install httpx [↪].

E.g. pip install httpx .

Import TestClient .

Create a TestClient by passing your FastAPI application to it.

Create functions with a name that starts with test_ (this is standard pytest conventions).

Use the TestClient object the same way as you do with httpx .

Write simple assert statements with the standard Python expressions that you need to check (again,
standard pytest ).

from fastapi import FastAPI


from fastapi.testclient import TestClient

app = FastAPI()

@app.get("/")
async def read_main():
return {"msg": "Hello World"}

client = TestClient(app)

def test_read_main():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"msg": "Hello World"}

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Tip

Notice that the testing functions are normal def , not async def .

And the calls to the client are also normal calls, not using await .

This allows you to use pytest directly without complications.

Technical Details

You could also use from starlette.testclient import TestClient .

FastAPI provides the same starlette.testclient as fastapi.testclient just as a convenience for you, the developer. But it
comes directly from Starlette.

Tip

If you want to call async functions in your tests apart from sending requests to your FastAPI application (e.g. asynchronous
database functions), have a look at the Async Tests ↪ in the advanced tutorial.

Separating tests
In a real application, you probably would have your tests in a different file.

And your FastAPI application might also be composed of several files/modules, etc.

FastAPI app file

Let's say you have a file structure as described in Bigger Applications ↪:

.
├── app
│ ├── __init__.py
│ └── main.py

In the file main.py you have your FastAPI app:

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
async def read_main():
return {"msg": "Hello World"}

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Testing file

Then you could have a file test_main.py with your tests. It could live on the same Python package (the
same directory with a __init__.py file):

.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py

Because this file is in the same package, you can use relative imports to import the object app from the
main module ( main.py ):

from fastapi.testclient import TestClient

from .main import app

client = TestClient(app)

def test_read_main():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"msg": "Hello World"}

...and have the code for the tests just like before.

Testing: extended example


Now let's extend this example and add more details to see how to test different parts.

Extended FastAPI app file

Let's continue with the same file structure as before:

.
├── app
│ ├── __init__.py
│ ├── main.py
│ └── test_main.py

Let's say that now the file main.py with your FastAPI app has some other path operations.

It has a GET operation that could return an error.

It has a POST operation that could return several errors.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Both path operations require an X-Token header.

Python 3.10+ Python 3.9+ Python 3.8+ Python 3.10+ non-Annotated Python 3.8+ non-Annotated

from typing import Annotated

from fastapi import FastAPI, Header, HTTPException


from pydantic import BaseModel

fake_secret_token = "coneofsilence"

fake_db = {
"foo": {"id": "foo", "title": "Foo", "description": "There goes my hero"},
"bar": {"id": "bar", "title": "Bar", "description": "The bartenders"},
}

app = FastAPI()

class Item(BaseModel):
id: str
title: str
description: str | None = None

@app.get("/items/{item_id}", response_model=Item)
async def read_main(item_id: str, x_token: Annotated[str, Header()]):
if x_token != fake_secret_token:
raise HTTPException(status_code=400, detail="Invalid X-Token header")
if item_id not in fake_db:
raise HTTPException(status_code=404, detail="Item not found")
return fake_db[item_id]

@app.post("/items/", response_model=Item)
async def create_item(item: Item, x_token: Annotated[str, Header()]):
if x_token != fake_secret_token:
raise HTTPException(status_code=400, detail="Invalid X-Token header")
if item.id in fake_db:
raise HTTPException(status_code=400, detail="Item already exists")
fake_db[item.id] = item
return item

Extended testing file

You could then update test_main.py with the extended tests:

from fastapi.testclient import TestClient

from .main import app

client = TestClient(app)

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
def test_read_item():
response = client.get("/items/foo", headers={"X-Token": "coneofsilence"})
assert response.status_code == 200
assert response.json() == {
"id": "foo",
"title": "Foo",
"description": "There goes my hero",
}

def test_read_item_bad_token():
response = client.get("/items/foo", headers={"X-Token": "hailhydra"})
assert response.status_code == 400
assert response.json() == {"detail": "Invalid X-Token header"}

def test_read_nonexistent_item():
response = client.get("/items/baz", headers={"X-Token": "coneofsilence"})
assert response.status_code == 404
assert response.json() == {"detail": "Item not found"}

def test_create_item():
response = client.post(
"/items/",
headers={"X-Token": "coneofsilence"},
json={"id": "foobar", "title": "Foo Bar", "description": "The Foo Barters"},
)
assert response.status_code == 200
assert response.json() == {
"id": "foobar",
"title": "Foo Bar",
"description": "The Foo Barters",
}

def test_create_item_bad_token():
response = client.post(
"/items/",
headers={"X-Token": "hailhydra"},
json={"id": "bazz", "title": "Bazz", "description": "Drop the bazz"},
)
assert response.status_code == 400
assert response.json() == {"detail": "Invalid X-Token header"}

def test_create_existing_item():
response = client.post(
"/items/",
headers={"X-Token": "coneofsilence"},
json={
"id": "foo",
"title": "The Foo ID Stealers",
"description": "There goes my stealer",
},
)

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
assert response.status_code == 409
assert response.json() == {"detail": "Item already exists"}

Whenever you need the client to pass information in the request and you don't know how to, you can search
(Google) how to do it in httpx , or even how to do it with requests , as HTTPX's design is based on
Requests' design.

Then you just do the same in your tests.

E.g.:

To pass a path or query parameter, add it to the URL itself.

To pass a JSON body, pass a Python object (e.g. a dict ) to the parameter json .

If you need to send Form Data instead of JSON, use the data parameter instead.

To pass headers, use a dict in the headers parameter.

For cookies, a dict in the cookies parameter.

For more information about how to pass data to the backend (using httpx or the TestClient ) check the
HTTPX documentation [↪].

Info

Note that the TestClient receives data that can be converted to JSON, not Pydantic models.

If you have a Pydantic model in your test and you want to send its data to the application during testing, you can use the
jsonable_encoder described in JSON Compatible Encoder ↪.

Run it
After that, you just need to install pytest :

bash

fast →
$ pip install pytest

███ 8%

It will detect the files and tests automatically, execute them, and report the results back to you.

Run the tests with:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
bash

fast →
$ pytest

================ test session starts ================


platform linux -- Python 3.6.9, pytest-5.3.5, py-1.8.1, pluggy-0.13.1
rootdir: /home/user/code/superawesome-cli/app
plugins: forked-1.1.3, xdist-1.31.0, cov-2.8.1
collected 6 items

█████████████ 33%

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Debugging

You can connect the debugger in your editor, for example with Visual Studio Code or PyCharm.

Call uvicorn
In your FastAPI application, import and run uvicorn directly:

import uvicorn
from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def root():
a = "a"
b = "b" + a
return {"hello world": b}

if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)

About __name__ == "__main__"

The main purpose of the __name__ == "__main__" is to have some code that is executed when your file is
called with:

bash

$ python myapp.py

restart ↻

but is not called when another file imports it, like in:

from myapp import app

More details

Let's say your file is named myapp.py .

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
If you run it with:

bash

$ python myapp.py

restart ↻

then the internal variable __name__ in your file, created automatically by Python, will have as value the
string "__main__" .

So, the section:

uvicorn.run(app, host="0.0.0.0", port=8000)

will run.

This won't happen if you import that module (file).

So, if you have another file importer.py with:

from myapp import app

# Some more code

in that case, the automatically created variable inside of myapp.py will not have the variable __name__
with a value of "__main__" .

So, the line:

uvicorn.run(app, host="0.0.0.0", port=8000)

will not be executed.

Info

For more information, check the official Python docs [↪].

Run your code with your debugger

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Because you are running the Uvicorn server directly from your code, you can call your Python program (your
FastAPI application) directly from the debugger.

For example, in Visual Studio Code, you can:

Go to the "Debug" panel.

"Add configuration...".

Select "Python"

Run the debugger with the option " Python: Current File (Integrated Terminal) ".

It will then start the server with your FastAPI code, stop at your breakpoints, etc.

Here's how it might look:

If you use Pycharm, you can:

Open the "Run" menu.

Select the option "Debug...".

Then a context menu shows up.

Select the file to debug (in this case, main.py ).

It will then start the server with your FastAPI code, stop at your breakpoints, etc.

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF
Here's how it might look:

Explore our developer-friendly HTML to PDF API Printed using PDFCrowd HTML to PDF

You might also like