Introduction to FastAPI

Introduction

In an increasingly data-driven world, the need for effective and efficient web frameworks to build APIs has never been greater. One such framework that has been gaining popularity in the Python community is FastAPI. This powerful, modern, web framework is specifically designed for building APIs quickly and easily, yet it remains as powerful as you need it to be.

FastAPI is built on Starlette (for the web parts) and Pydantic (for the data parts), allowing it to offer some quite compelling features. It has lightning-fast request parsing and model validation thanks to Pydantic. It also supports modern, Pythonic async def syntax for asynchronous tasks and offers robust security and authentication features out-of-the-box. In addition, FastAPI provides automatic interactive API documentation, allowing you and your team to quickly understand and test your API.

FastAPI isn't the only player in the field, of course. Flask and Django have been the go-to choices for Python web development for many years. But FastAPI's unique blend of speed, ease of use, and out-of-the-box features are starting to turn heads.

This guide should serve you as a starting point for exploring the world of FastAPI. Here, we will take you on a journey to understand FastAPI from the basics, through more advanced topics, to best practices.

Installation and Setup

To work with FastAPI, you will first need a functioning Python environment. If you haven’t already, download and install the latest version of Python from the official website. As of the writing of this article, Python 3.9 or newer is recommended to enjoy all the benefits that FastAPI has to offer.

FastAPI is available on the Python Package Index (PyPI) and can be installed with pip, Python's default package manager. You should also install uvicorn, an ASGI server, to serve your FastAPI application:

$ pip install fastapi
$ pip install uvicorn

Setting up a New FastAPI Project

Once FastAPI and uvicorn are installed, you're ready to start your new FastAPI project. Begin by creating a new directory for your project:

$ mkdir fastapi-project
$ cd fastapi-project

In the project directory, create a new Python file, such as main.py. This will serve as the entry point for your FastAPI application. Here's the most basic FastAPI application possible:

from fastapi import FastAPI

app = FastAPI()

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

This script creates a new FastAPI application and defines a single route, /, that accepts GET requests and returns a simple JSON response.

Running your FastAPI Application

To run your FastAPI application, use the uvicorn command followed by the name of your module (Python file without the .py extension), and the variable that is your FastAPI application:

$ uvicorn main:app --reload

The --reload flag enables hot reloading, which means the server will automatically update whenever you make changes to your Python files.

Navigate to http://localhost:8000 in your web browser, and you should see the JSON response from your FastAPI application: {"Hello": "World"}.

A Brief Tour of a FastAPI Application

The main.py file you've created is the core of your FastAPI application. As your application grows, you'll add more route functions to this file (or to other modules), each defining a different endpoint of your API. Route functions can accept parameters, request bodies, and can return a variety of different responses.

In the next section, we'll explore the basics of FastAPI, such as defining endpoints and working with different types of requests and responses.

Basics of FastAPI

FastAPI takes a simple yet powerful approach to building APIs. At the core of this approach are Python's type annotations, which allow FastAPI to automatically handle much of the validation, serialization, and documentation for your API.

Defining Endpoints

In FastAPI, an endpoint is defined as a Python function decorated with a route decorator. The route decorator, such as @app.get(), specifies the HTTP method and path for the endpoint:

from fastapi import FastAPI

app = FastAPI()

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

This defines a GET endpoint at the path /items/{item_id}, where {item_id} is a path parameter. The function read_item() will be called whenever a request is made to this endpoint.

Path Parameters and Query Parameters

In the previous example, the path parameter item_id is automatically interpreted as an integer because of the : int type annotation. FastAPI will validate that this parameter is an integer and convert it from a string before passing it to your function.

Query parameters are defined as function parameters. For instance, to add a query parameter q, you could modify the read_item() function as follows:

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

Here q is an optional query parameter, with a default value of None.

Request Bodies

To define a POST endpoint that accepts a request body, you can use Pydantic models. A Pydantic model is a Python class that extends pydantic.BaseModel and defines a set of attributes, each with a type annotation:

from pydantic import BaseModel

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

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

In this example, the create_item() function accepts one parameter, item, which is expected to be an instance of the Item model. FastAPI will automatically validate the request body to ensure it matches the model, convert the JSON into an Item instance, and pass this instance to your function.

Response Bodies

In FastAPI, the return value of a route function is automatically converted to JSON and returned as the body of the HTTP response. You can return almost any type of value, including Pydantic models, lists, dictionaries, etc:

@app.get("/ping")
def ping():
    return "pong"

Here, the string "pong" will be returned as a JSON response ("pong").

These code examples should help you understand the basics of FastAPI. As you progress through the article, you can apply these principles to more complex examples and your own FastAPI applications. In the next section, we'll delve into the advanced topics of FastAPI, including error handling, dependency injection, security, and more.

Advanced Topics in FastAPI

While FastAPI is easy to pick up and get started with, it also has a wealth of advanced features that make it powerful and flexible. In this section, we'll discuss some of these advanced topics.

Using Pydantic Models for Data Validation and Serialization

FastAPI heavily relies on Pydantic models, not only for validating request bodies, but also for validating response data, query parameters, path parameters, and more. Pydantic's use of Python type annotations makes the data validation simple, clear, and easy to understand:

from pydantic import BaseModel, Field

class Item(BaseModel):
    name: str = Field(..., example="Foo")
    description: str = Field(None, example="A very nice Item")
    price: float = Field(..., example=35.4)
    tax: float = Field(None, example=3.2)

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

The Field function is used to add extra validation to the Pydantic model fields and provide example values.

Handling Errors and Exceptions

FastAPI provides a way to handle errors and exceptions in a unified manner. You can use HTTPException to return HTTP error responses:

from fastapi import HTTPException

@app.get("/items/{item_id}")
def read_item(item_id: int):
    if item_id not in items:
        raise HTTPException(status_code=404, detail="Item not found")
    return {"item_id": item_id}

Dependency Injection

FastAPI has a simple but powerful dependency injection system. It is based on Python type annotations and does not require any complex configurations:

Free eBook: Git Essentials

Check out our hands-on, practical guide to learning Git, with best-practices, industry-accepted standards, and included cheat sheet. Stop Googling Git commands and actually learn it!

from fastapi import Depends

def get_db():
    db = ...
    try:
        yield db
    finally:
        db.close()

@app.get("/items/{item_id}")
def read_item(item_id: int, db = Depends(get_db)):
    item = db.get(item_id)
    return item

Security and Authentication

FastAPI provides several security tools in the box, like OAuth2 with JWT tokens and hashing:

from fastapi import Depends, FastAPI, HTTPException
from fastapi.security import OAuth2PasswordBearer

app = FastAPI()

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

@app.get("/items/{item_id}")
async def read_item(token: str = Depends(oauth2_scheme)):
    return {"token": token}

In this example, the OAuth2PasswordBearer class is used to handle OAuth2. The /items/{item_id} endpoint requires an Authorization header with a Bearer token.

Integrating with Databases

FastAPI does not dictate which database you should use and can be used with any ORM or DB client:

from fastapi import Depends, FastAPI
from sqlalchemy.orm import Session

from . import crud, models, schemas
from .database import SessionLocal, engine

app = FastAPI()

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)
    ...

This example uses SQLAlchemy for database interaction. The get_db dependency provides a database session for each request and closes it when the request ends.

Unit Testing in FastAPI

FastAPI applications can be easily tested with TestClient from fastapi.testclient:

from fastapi.testclient import TestClient

def test_read_item():
    client = TestClient(app)
    response = client.get("/items/42")
    assert response.status_code == 200
    assert response.json() == {"item_id": 42}

This is a basic test that makes a GET request to the /items/42 endpoint and asserts that the response status code is 200 and the response body is {"item_id": 42}.

In the next section, we'll apply these concepts to a real-world application, showing you step by step how to build a basic CRUD application with FastAPI.

Real-World Application

In this section, we'll use all of the concepts we've learned thus far to create a real-world application: a CRUD (Create, Read, Update, Delete) API for a hypothetical bookstore. This application will allow us to create, retrieve, update, and delete books in the store's inventory.

Step 1 - Setting Up Your Project

Before we begin, make sure your project is set up correctly. Ensure that you have FastAPI and Uvicorn installed, and set up a new project directory if you haven't already.

Step 2 - Defining the Data Model

We'll start by defining a data model for the books in our store. For this, we'll use Pydantic's BaseModel class.

from pydantic import BaseModel

class Book(BaseModel):
    name: str
    author: str
    isbn: str

Step 3 - Setting Up In-Memory Database

Next, let's set up an in-memory database for our API. We'll use a simple Python dictionary to store our data:

books_db = {}

Note: In a real-world application, you'd likely use a proper database here.

Step 4 - Defining the API Endpoints

Now we can define the endpoints for our API.

To create a book, we'll need a POST endpoint at the path /books:

@app.post("/books/")
def create_book(book: Book):
    if book.isbn in books_db:
        raise HTTPException(status_code=400, detail="ISBN already exists")
    books_db[book.isbn] = book.dict()
    return books_db[book.isbn]

To retrieve a book, we'll need a GET endpoint at the path /books/{isbn}:

@app.get("/books/{isbn}")
def read_book(isbn: str):
    if isbn not in books_db:
        raise HTTPException(status_code=404, detail="Book not found")
    return books_db[isbn]

We'll need a PUT endpoint at the path /books/{isbn} to update a book:

@app.put("/books/{isbn}")
def update_book(isbn: str, book: Book):
    if isbn not in books_db:
        raise HTTPException(status_code=404, detail="Book not found")
    books_db[isbn] = book.dict()
    return books_db[isbn]

To delete a book, we'll need a DELETE endpoint at the path /books/{isbn}.

@app.delete("/books/{isbn}")
def delete_book(isbn: str):
    if isbn not in books_db:
        raise HTTPException(status_code=404, detail="Book not found")
    del books_db[isbn]
    return {"message": "Book deleted successfully!"}

By using FastAPI, you have built a CRUD API in no time at all. You now have a working application that is ready to be deployed and used.

FastAPI Ecosystem

In addition to FastAPI itself, there are a number of other tools and libraries that can enhance your FastAPI applications and make them more powerful and flexible.

SQLModel

SQLModel is a library for interacting with SQL databases using Python models. It's built on top of SQLAlchemy and Pydantic, and is designed to work well with FastAPI. If your application uses a SQL database, SQLModel can make it easier to define your data models and perform database operations.

FastAPI Users

FastAPI Users is a library that provides common user management features for FastAPI applications, such as user registration, login, password reset, and email verification. It supports a variety of authentication methods, including JWT, OAuth2, and HTTP Basic Auth.

FastAPI Permissions

FastAPI Permissions is a simple and effective library for managing permissions and roles in a FastAPI application. It allows you to define permissions in a declarative way and automatically checks them for each request.

FastAPI Utils

FastAPI Utils is a collection of utilities for FastAPI applications. It includes helpers for pagination, request IDs, responses, databases, and more.

Typer

Typer is a library for building command-line applications, created by the same author as FastAPI. It uses the same principles as FastAPI, and is designed to be easy to use and Pythonic. If your FastAPI application needs a command-line interface, Typer can be a great choice.

Starlette

FastAPI is built on top of Starlette, a lightweight ASGI framework. While FastAPI provides a high-level API for building web applications, Starlette provides the underlying tools for handling HTTP requests and responses, routing, websockets, and more. Understanding Starlette can give you a deeper understanding of FastAPI and how it works.

All of these tools and libraries can enhance your FastAPI applications and make them more powerful, flexible, and robust. By understanding the FastAPI ecosystem, you can make the most of FastAPI and all it has to offer.

Conclusion

In this introductory guide, we have journeyed through the world of FastAPI, starting from the basic concepts, venturing into more advanced topics, and finally, building a real-world application. FastAPI, with its speed, simplicity, and feature-rich nature, offers a great choice for modern web application development.

We explored the power of Pydantic models, the simplicity of defining endpoints, the advantages of automatic data validation, and the ease of error handling in FastAPI. We've also delved into advanced topics like dependency injection, security and authentication, and integration with databases.

In building a CRUD application, we've seen how all these components come together in a real-world scenario. Following best practices ensures maintainability and scalability of our applications.

Moreover, the vibrant ecosystem around FastAPI empowers you with tools for user management, permissions, command-line application building, and more, making it not just a standalone framework, but part of a powerful toolkit.

We hope this guide serves you as a helpful resource. Remember that the key to mastering FastAPI, like any other tool, is practice. So go ahead, build and deploy applications, solve real-world problems, and happy coding!

Last Updated: June 19th, 2023
Was this article helpful?

Improve your dev skills!

Get tutorials, guides, and dev jobs in your inbox.

No spam ever. Unsubscribe at any time. Read our Privacy Policy.

© 2013-2024 Stack Abuse. All rights reserved.

AboutDisclosurePrivacyTerms