The Basics of Routing
Routing in web development refers to the process of directing a request to the correct handler based on the URL and HTTP method (GET, POST, etc.). In FastAPI, routes are defined using decorators that specify the HTTP method for the function they decorate. These routes act as endpoints for API users to access different resources.
Using Decorators to Define Routes
FastAPI utilizes a straightforward yet powerful system of decorators to map specific functions in your Python code to HTTP routes. These decorators make it incredibly easy to connect your logic to the corresponding HTTP methods, streamlining the creation of RESTful interfaces. Here’s an expanded look at how these decorators work and some of the additional parameters you can use to fine-tune your API's behavior.
Common HTTP Method Decorators
-
@app.get()
: This decorator defines routes that accept GET requests. GET requests are generally used to retrieve data, whether fetching a single item by ID or a list of items based on query parameters. The data returned by GET routes is not supposed to alter the state of the application's data on the server. -
@app.post()
: POST requests are designed for creating new resources. When using the@app.post()
decorator, your API expects to receive data (usually through the request body), which it will use to create a new item in the database or another storage system. -
@app.put()
: Put requests are used for updating existing resources. The@app.put()
decorator helps you define routes that replace an existing item with a new one provided in the request body or update specific fields of an existing item. -
@app.delete()
: This decorator defines a route to handle DELETE requests, which are used to remove existing resources from your system.
Each of these decorators takes the route path as an argument along with optional parameters to control additional details, such as response status codes or tags. Here is a basic example:
@app.get("/")
async def home():
return {"message": "Welcome to our API!"}
Additional Parameters for Decorators
While the primary purpose of these decorators is to link a URL path to a Python function and assign an HTTP method, they also accept various optional parameters that allow you to control additional aspects of the request and response:
-
Status Codes: You can specify the HTTP status code to return for responses from this route. For example, using
status_code=201
in a@app.post()
decorator is common to indicate that a resource was created successfully. - Tags: Tags help you organize endpoints into groups in the generated documentation, making it easier for developers to navigate the API.
- Responses: You can define custom response classes that dictate how the response should be formatted. This can be useful for setting cache controls, media types, and other headers.
- Dependencies: Dependencies can be declared for individual operations. These are execution dependencies or security dependencies that must be met before the actual route function is called.
Example with Advanced Decorator Use
Here’s a more advanced example using some of these optional parameters:
from fastapi import status
@app.post("/items/", status_code=status.HTTP_201_CREATED,
tags=["Item Operations"])
async def create_item(item: Item):
"""
Create an item in the database.
"""
db.add(item)
return item
In this example:
- The route is set to handle POST requests aimed at creating a new item.
- It uses the
status_code
parameter to explicitly set the HTTP status code to 201, which clients interpret as "Created." - The route is tagged as "Item Operations," which helps organize this endpoint under a specific group in the API documentation.
Understanding and utilizing these decorator options in FastAPI not only helps in creating a functional API but also enhances its maintainability and usability, especially in complex systems where good documentation and clear organization are important.
Path Parameters
Path parameters, also known as route parameters, are dynamic parts of the URL path that are used to capture specific values from the URL. They are essential for creating RESTful APIs that respond to specific resources or actions based on the URL.
Capturing Dynamic Segments in the URL
FastAPI allows you to define routes that can dynamically accept parts of the URL as parameters. This functionality is particularly useful when you need to perform actions on specific resources, like retrieving, updating, or deleting a particular user or item based on an ID.
Defining Path Parameters
To define a path parameter in FastAPI, you include a variable in the route's path enclosed in curly braces {}
. Here’s how you can define and use a path parameter in a FastAPI route:
@app.get("/users/{user_id}")
async def get_user(user_id: int):
# FastAPI automatically extracts `user_id` from the URL path and injects it into the function as an integer.
return {"user_id": user_id}
In this example, user_id
is a path parameter integrated directly into the URL path. When a request is made to this endpoint, FastAPI captures the user_id
segment of the URL, converts it to an integer (thanks to the type hint), and passes it to the get_user
function.
Automatic Data Conversion
FastAPI utilizes Python's type hints to automatically convert URL parameters to the specified types. This not only simplifies the function implementation by ensuring that parameters are of the expected type but also adds a layer of validation:
- If the parameter can be successfully converted to the specified type, FastAPI passes it to your function.
- If the conversion fails (e.g., a non-integer string is passed where an integer is expected), FastAPI will automatically respond with a 422 Unprocessable Entity error, indicating that something was wrong with the user input.
Handling Multiple Path Parameters
You can define routes that capture more than one path parameter. This is particularly useful for nested resources or more complex URL structures:
@app.get("/items/{category_id}/products/{product_id}")
async def get_product(category_id: int, product_id: int):
return {"category_id": category_id, "product_id": product_id}
In this route, both category_id
and product_id
are captured as path parameters. This method allows APIs to address resources hierarchically and contextually, enhancing the navigational logic within the API.
Benefits of Using Path Parameters
Using path parameters can improve the intuitiveness and ease of use of your API. Your API endpoints can reflect the hierarchical structure of your data, making it easier to understand how resources are organized and accessed. This structure is critical in the development of scalable and maintainable web applications that can handle complex data models and user interactions.
Type Hinting for Automatic Data Conversion
FastAPI takes advantage of Python's robust type hints to automatically handle data conversion, validation, and error handling. This functionality is especially useful when using path parameters and query parameters since it guarantees that the data received by your functions is precisely what it's supposed to be.
How Type Hinting Works in FastAPI
When you define a function in FastAPI, any path or query parameters can be given a specific type through Python's type annotations. FastAPI uses these annotations to validate and convert incoming data before it ever reaches your function. Here's a closer look at the process:
-
Validation: FastAPI checks if the incoming parameters conform to the types specified in your function's signature. If a parameter is expected to be an integer (as specified by
int
), but the request provides a string that cannot be converted into an integer, FastAPI will block the request. -
Conversion: If the validation is successful, FastAPI converts the parameter to the specified type. For example, if your route expects an
int
and the user provides a numeric string via the URL, FastAPI automatically converts this string to an integer. - Error Handling: If either validation or conversion fails, FastAPI does not call your function. Instead, it returns a 422 Unprocurable Entity response directly to the client. This response includes a JSON body that details what went wrong, helping the client correct their request.
This mechanism not only prevents common data-related bugs but also enhances security by ensuring that data types are enforced before any processing takes place.
Query Parameters
Query parameters allow clients to pass optional or additional information to API endpoints. They are used extensively in APIs for filtering, sorting, and pagination purposes.
Passing Optional Data in the URL
Query parameters are defined in FastAPI by setting default values in the function parameters. FastAPI interprets any default-value parameters in a route's function as query parameters. This design allows you to easily specify what data your API can accept without having to manually parse and handle URL queries.
Here’s how you can handle optional query parameters more effectively:
@app.get("/items/")
async def read_items(skip: int = 0, limit: int = 10, search: Optional[str] = None):
items = fetch_items(skip=skip, limit=limit, search_query=search)
return {"items": items, "skip": skip, "limit": limit}
In this revised example:
-
skip
andlimit
help with pagination, as in the previous example. -
search
is an optional parameter that clients can use to filter items based on a search query. Ifsearch
is not provided, it defaults toNone
, which thefetch_items
function can interpret as no filter applied.
Benefits of Using Query Parameters
Using query parameters in FastAPI allows for flexible, powerful APIs that can adapt to a variety of client needs:
- Flexibility: Clients can modify their requests to include only the information they need, reducing bandwidth and processing time.
- Functionality: APIs can offer extensive customizability, such as filtering, sorting, and precise pagination, enhancing user experience.
- Simplicity: FastAPI's automatic handling of query parameters reduces boilerplate code, making the API easier to develop and maintain.
This concludes Part 2 of our FastAPI tutorial, where you learned about routing, path parameters, and query parameters. These fundamentals are essential for building robust and dynamic APIs. In the next part, we’ll explore how to handle requests and responses more deeply, looking at request bodies and response models. Stay tuned!
Code
from fastapi import FastAPI
app = FastAPI()
# Basic routing with an HTTP GET method to return a welcome message
@app.get("/")
async def home():
return {"message": "Welcome to our API!"}
@app.post("/items/", status_code=status.HTTP_201_CREATED, tags=["Item Operations"])
async def create_item(item: Item):
"""
Create an item in the database.
"""
db.add(item)
return item
# Defining a route that captures a path parameter
@app.get("/users/{user_id}")
async def get_user(user_id: int):
# FastAPI automatically validates and converts `user_id` to an integer
return {"user_id": user_id}
@app.get("/items/{category_id}/products/{product_id}")
async def get_product(category_id: int, product_id: int):
return {"category_id": category_id, "product_id": product_id}
# Defining a route that uses query parameters to filter data (e.g., for pagination)
@app.get("/items/")
async def read_items(skip: int = 0, limit: int = 10, search: Optional[str] = None):
items = fetch_items(skip=skip, limit=limit, search_query=search)
return {"items": items, "skip": skip, "limit": limit}
# Running the API with Uvicorn. This command should be in a separate runner file or in the command line.
# `uvicorn main:app --reload` where `main` is the name of your Python file.
Stay Tuned for Part 3
In the upcoming Part 3, we’ll delve deeper into one of FastAPI’s most powerful features: Pydantic Data Models. We will explore how Pydantic enhances data validation, parsing, and type safety, making your API development smoother and more error-proof.
We'll cover:
- Why Pydantic? Understand the benefits of using Pydantic for data validation and type safety in your FastAPI applications.
-
Defining Pydantic Models: Learn how to create robust models using Pydantic’s
BaseModel
and explore advanced features such as field types, validators, and nested models. - Using Pydantic Models in API Endpoints: I
- Integrating these models can simplify data handling, improve error handling, and ensure your API endpoints are type-safe and easy to understand.
Leave comments about what you would like me to cover next
If you would like to support me or buy me a beer feel free to join my Patreon jamesbmour
Top comments (1)
Nice !