Today, we will explore FastAPI, an ultrafast and modern web framework for building APIs in Python. It’s time to unleash the power of FastAPI and discover how it can make your web development journey smoother and more efficient.
FastAPI is an emerging star in the Python web framework galaxy. It is a modern, fast (high-performance) web framework for building APIs with Python 3.7+ based on standard Python type hints. This tutorial will explore FastAPI’s key features, and by the end of it, you will have a good grasp of FastAPI and be able to build high-performance APIs with it.
What is FastAPI?
FastAPI is an open-source web framework that aspires to be easy to use, fast to code, and highly performant. It was created by Sebastián Ramírez and released to the public in 2018. Since then, it has been rapidly gaining popularity due to its impressive performance and excellent developer experience.
FastAPI is built on Starlette, a lightweight ASGI framework, for web routing, and Pydantic for data validation and serialization. This makes FastAPI ideal for building HTTP/REST APIs. It allows you to create endpoints using Python’s async
and await
keywords, ensuring that your API can handle a large number of requests simultaneously.
FastAPI’s use of type hints has many benefits. For one, it enables excellent editor support, including autocompletion and type checks. This makes the development process easier and more efficient. Second, it offers automatic request and response data validation, serialization, and documentation, leading to fewer errors and bugs in the API.
Setting Up Your FastAPI Environment
To get started with FastAPI, you’ll need to install it. FastAPI is available on PyPI, and you can install it using pip, Python’s package manager. Along with FastAPI, you’ll also need an ASGI server. Uvicorn is a lightning-fast ASGI server that is often used with FastAPI.
Here’s the command to install FastAPI and Uvicorn:
pip install fastapi uvicorn
After the installation is complete, you can create your first FastAPI application. Create a new Python file named main.py
and write the following code:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"Hello": "FastAPI"}
In this code, you create a new FastAPI application and define a route for the root (“/”) URL. When a client sends a GET request to the root URL, FastAPI calls the read_root
function and sends its return value as the HTTP response. The @app.get("/")
decorator is used to associate the read_root
function with GET requests to the root URL.
To start the server, navigate to the directory containing the main.py
file in your terminal and run this command:
uvicorn main:app --reload
The uvicorn main:app
command tells Uvicorn to create an ASGI application from the FastAPI application in the main.py
file, and the --reload
flag enables hot reloading, which means the server will automatically update whenever you make changes to your code.
Delving Deeper: Building a Full-Fledged API with FastAPI
FastAPI’s power truly shines when you’re building complex APIs. In this section, we’ll build an API for a simple note-taking application. This application will allow users to create, read, update, and delete notes.
Data modeling with Pydantic
FastAPI uses Pydantic for data validation. Pydantic allows you to define data models using Python’s type annotations. These models are automatically validated by FastAPI when receiving and sending data, ensuring that your API always works with the correct data.
Let’s create a simple Note
model for our note-taking application using Pydantic:
from pydantic import BaseModel
class Note(BaseModel):
title: str
content: str
This Note
model has two fields: title
and content
, both of which are of type str
. FastAPI will use this model to validate incoming note data and to generate JSON Schema for the API documentation.
CRUD operations with FastAPI
With our data model defined, we can now create the API endpoints for creating, reading, updating, and deleting notes. For simplicity, we’ll store the notes in a list, but in a real-world application, you would typically use a database.
Start by initializing an empty list to store the notes:
notes = []
Now, let’s create the GET
and POST
methods for our notes API:
@app.get("/notes/", response_model=List[Note])
async def read_notes():
return notes
@app.post("/notes/", response_model=Note)
async def create_note(note: Note):
notes.append(note)
return note
The read_notes
function will return all the notes we have stored, and the create_note
function will create a new note. Note how we use our Note
model as a type hint. FastAPI will automatically validate the request data and construct a Note
instance for us.
Let’s now add the PUT
and DELETE
methods:
@app.put("/notes/{note_id}", response_model=Note)
async def update_note(note_id: int, note: Note):
if note_id < 0 or note_id >= len(notes):
raise HTTPException(status_code=404, detail="Note not found")
notes[note_id] = note
return note
@app.delete("/notes/{note_id}")
async def delete_note(note_id: int):
if note_id < 0 or note_id >= len(notes):
raise HTTPException(status_code=404, detail="Note not found")
del notes[note_id]
return {"status": "success"}
The update_note
function will replace a note with a new one, and the delete_note
function will delete a note. Note how we use Python’s built-in raise
keyword to return a 404 error if the note does not exist. FastAPI automatically converts the HTTPException
into a proper HTTP response.
Advanced Features of FastAPI
FastAPI provides several advanced features that can significantly enhance your web API capabilities. Here, we will discuss some of them.
Query Parameters and String Validations
FastAPI allows you to use function parameters that aren’t part of your path parameters as query parameters. FastAPI also provides a way to validate these parameters using Pydantic models:
from fastapi import Query
@app.get("/items/")
async def read_items(q: str = Query(None, min_length=3, max_length=50, regex="^[a-z]+$")):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
In this example, the q
parameter is a query parameter that’s validated by the Query
function. The min_length
, max_length
, and regex
parameters impose validation checks on the value of q
.
Path Parameters and Numeric Validations
FastAPI also provides a way to validate path parameters using Pydantic models. This comes in handy when you want to ensure that the path parameters in your route conform to certain rules:
from fastapi import Path
@app.get("/items/{item_id}")
async def read_items(item_id: int = Path(..., title="The ID of the item to get", gt=0)):
results = {"item_id": item_id}
return results
In this example, item_id
a path parameter is validated by the Path
function. The gt
(greater than) parameter imposes a validation check that the item_id
must be greater than 0.
Dependency Injection
FastAPI supports dependency injection, which is a technique to achieve Inversion of Control between classes and their dependencies. Here’s an example:
from fastapi import Depends
from db_session import DBSession
def get_db():
db = DBSession()
try:
yield db
finally:
db.close()
@app.get("/items/")
async def read_items(db: DBSession = Depends(get_db)):
items = db.get_items()
return {"items": items}
In this example, get_db
is a dependency function that manages a database session. When read_items
is called, FastAPI will call get_db
first and pass its result to read_items
.
Dockerizing your FastAPI application
Docker is a popular tool for containerizing applications, making them portable and easy to deploy. Here’s a basic Dockerfile
for a FastAPI application:
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.7
WORKDIR /app
COPY ./requirements.txt /app/requirements.txt
RUN pip install -r requirements.txt
COPY ./app /app
This Dockerfile
starts with a base image that already has FastAPI, Uvicorn, and Gunicorn installed, sets /app
as the working directory, copies the requirements.txt
file and installs any additional requirements, and finally copies the application code into the container.
You can build this Docker image by running the following command in the same directory as your Dockerfile
:
docker build -t my-fastapi-app .
And then you can run your application with this command:
docker run -d -p 80:80 my-fastapi-app
This will start your FastAPI application in a Docker container, accessible at http://localhost
.
Deploying to Cloud Platforms
Once you’ve dockerized your FastAPI application, you can easily deploy it to various cloud platforms that support Docker containers, such as Heroku, AWS, Google Cloud, Azure, etc. The steps for deploying will vary depending on the platform, but generally involve pushing your Docker image to a container registry, and then creating and configuring a new service or application using that image.
For instance, to deploy to Heroku, you would need to create a heroku.yml
file in your project directory with the following content:
build:
docker:
web: Dockerfile
run:
web: uvicorn main:app --host=0.0.0.0 --port=${PORT:-5000}
Then, you can create a new Heroku application and push your code:
heroku create
git push heroku master
Heroku will automatically build and deploy your application using the heroku.yml
file.
Conclusion
Congratulations on completing this FastAPI tutorial! You’ve learned about the key features of FastAPI, how to set up your development environment, how to build a full-fledged API, and how to leverage advanced features like query and path parameters validation, dependency injection, and more. You’ve also seen how to containerize your application using Docker and deploy it to a cloud platform.
FastAPI is a powerful and flexible framework for building web APIs. Its emphasis on performance, ease of use, and developer experience sets it apart from other Python web frameworks. Whether you’re a beginner looking to build your first API or an experienced developer wanting to take your skills to the next level, FastAPI is definitely worth exploring.
Remember, the journey of mastering FastAPI doesn’t stop here. There’s so much more to learn and explore. So keep coding, keep exploring, and as always, Happy Pythoning!