Build a REST API with FastAPI

Let he who has never coded an API cast the first stone. Indeed, APIs have been everywhere for quite a few years. In this tutorial, we will explore the basic concepts of FastAPI and how to use it to quickly create REST APIs.
By the end of this tutorial, you will be able to create production-ready APIs (getting a bit ahead of myself here...) and will have the necessary knowledge to dive deeper into your specific use cases (But later on, AI can assist you).
FastAPI: What is it?
FastAPI is a modern, fast, and high-performance framework for building APIs in Python, based on standard Python type annotations.
Basically, when you want to build an API in Python, there's a good chance you'll use FastAPI (Well, there are alternatives: Flask, Django Rest Framework, Tornado, ...).
FastAPI offers high performance, comparable to NodeJS and Go. It speeds up development, reduces human errors, and provides great editor support with auto-completion, minimizing debugging time. The framework is easy to learn, reduces code duplication, and ensures production-ready code with automatic interactive documentation. Based on open standards like OpenAPI and JSON Schema, FastAPI simplifies the process of building robust and efficient APIs.
Install FastAPI
Like any Python project, it's best to start by creating a virtual environment (optional), and then install FastAPI
$ python -m pip install fastapi[standard] uvicorn[standard]
Your first API: Just the basics
In this tutorial, we’ll see how to create a simple "products" API and cover the basics of retrieving and updating products. This is a fake API — we won’t be handling data persistence, as that’s beyond the scope of this FastAPI introduction.
FastAPI is very intuitive: it provides a decorator for each HTTP method. Here’s a summary table:
HTTP method | FastAPI Decorator |
---|---|
GET | @app.get() |
POST | @app.post() |
PUT | @app.put() |
DELETE | @app.delete() |
PATCH | @app.patch() |
OPTIONS | @app.options() |
TRACE | @app.trace() |
Get
Create a file main.py
with the following basic FastAPI content:
from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"msg": "Hello !"}
Run it
fastapi dev main.py
Open your browser at http://127.0.0.1:8000
{"msg":"Hello !"}
And there you go, your API is now available and already returns a JSON object. For now, it doesn't do much — but we’re about to start building our products API:
from typing import Union
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"msg": "Hello !"}
@app.get("/products/{product_id}")
def read_product(product_id: int, name: Union[str, None] = None):
return {"id": product_id, "name": name}
Here's a bit of explanation:
product_id
is a path parameter and gets passed to the function as theproduct_id
argument.name
is a query parameter. Any function parameter that isn’t in the path is treated as a query parameter by default.
Since we set it toNone
, it’s optional!
Open your browser at http://127.0.0.1:8000/products/5?name=T-shirt.
{"id":5,"name":"T-shirt"}
And there you go, we've already implemented our products API for the GET endpoint. (As mentioned before, I'm not handling where the data is stored — that's up to you!)
Update
Now, let's move on to the update part:
from typing import Union
from fastapi import FastAPI
from pydantic import BaseModel
class Product(BaseModel):
name: str
price: float
is_offer: Union[bool, None] = None
app = FastAPI()
@app.put("/products/{product_id}")
def update_product(product_id: int, product: Product):
return {"id": product_id, "name": product.name, "price": 10.5}
Here's a bit of explanation:
product (Product)
the product itself. We directly use the class that represents the resource! It's handy!
Execute the following request:
PUT /products/5 HTTP/1.1
Host: 127.0.0.1:8000
Content-Type: application/json
The response is:
{
"id": 5,
"name": "new name",
"price": 10.5
}
A few more things
Data Validation
If you point your browser to http://127.0.0.1:8000/products/one
, then you’ll see a nice HTTP error:
{"detail":[{"type":"int_parsing","loc":["path","product_id"],"msg":"Input should be a valid integer, unable to parse string as an integer","input":"one"}]}
FastAPI takes care of the validation right away.
Filtering
from typing import Union, List
from fastapi import FastAPI, Query
from pydantic import BaseModel
class Product(BaseModel):
name: str
price: float
is_offer: Union[bool, None] = None
app = FastAPI()
# Simulated in-memory database
fake_items_db = [
{"product_id": 1, "name": "Item A", "price": 25.5, "is_offer": False},
{"product_id": 2, "name": "Item B", "price": 35.0, "is_offer": True},
{"product_id": 3, "name": "Item C", "price": 45.0, "is_offer": None},
{"product_id": 4, "name": "Item D", "price": 55.5, "is_offer": False},
]
@app.get("/products")
def get_products(
name: Union[str, None] = None,
min_price: Union[float, None] = None,
max_price: Union[float, None] = None,
is_offer: Union[bool, None] = None,
page: int = 1,
page_size: int = 10
):
# Filtering based on query parameters
results = fake_items_db
if name:
results = [for product in results if name.lower() in product["name"].lower()]
if min_price is not None:
results = [for product in results if product["price"] >= min_price]
if max_price is not None:
results = [for product in results if product["price"] <= max_price]
if is_offer is not None:
results = [for product in results if product["is_offer"] == is_offer]
# Pagination
start = (page - 1) * page_size
end = start + page_size
paginated_results = results[start:end]
return {"products": paginated_results, "total": len(results), "page": page, "page_size": page_size}
Nothing fancy here — just a few optional query parameters for filtering.
Open your browser at http://127.0.0.1:8000/products/?name=Item A.
{"products":[{"product_id":1,"name":"Item A","price":25.5,"is_offer":false}],"total":1,"page":1,"page_size":10}
API docs
Documentation… what a chore to write it. You'll love FastAPI — it's auto-generated, which makes life so much better!
Open your browser at http://127.0.0.1:8000/docs, you will see the automatic interactive API documentation (provided by Swagger UI):

Alternative provided by ReDoc: http://127.0.0.1:8000/redoc.
Conclusion
As we've just seen, it's quick and easy to create an API with FastAPI. In just a few lines of code, our API is already up and running!
We haven't covered everything, but we’ve gone trough the basic principles. If you want to dive deeper, you can check out the FastAPI documentation:
Laisser un commentaire