Routing in FastAPI

Routing is the foundation of web applications and APIs. The path and the type of HTTP method request the client sends determines how the server will respond.

In this post we look at how FastAPI handles routing by adding create, read, update, and delete operations to our ice cream inventory app.

The contents of this post are a continuation of a series of posts on FastAPI. Visit the FastAPI page to see what has been covered already. The GitHub folder named 02-path-and-query-parameters is where we’ll be starting.

The GET Request

We have already covered the GET request in the previous post titled Path and Query Parameters.

In our main.py file we have three different GET paths defined that return different responses. In the example below, we return the inventory item that matches the parameter path id value.

@app.get("/items/{id}")
def get_item(id: int):
	return inventory[id]

There are additional things we can do with the GET request path, such as reading the header information, adding dependencies, or requiring authentication. We’ll get to these items in later posts.

The POST Request

The POST request method is used to send data to the web server. Client applications can send data in the body of a request. This is commonly done when filling out webforms and so on. However, this can create a source of problems as well.

Whenever a client is sending data to your application, you must ensure the data is correct and valid. Using the wrong data type or out of range values may crash an application if it’s not accounted for.

FastAPI handles data validation with Pydantic, a parsing library that allows you to build data models that conform to the types and constraints that you define.

To create our data model, import the BaseModel class from pydantic.

from pydantic import BaseModel

Then create a class for Item that inherits from BaseModel.

class Item(BaseModel):
	flavor: str
	description: str
	price: float
	vegan: bool
	quantity: int

The POST route path is created with the @app.post() path operation decorator as shown below.

@app.post("/items/")
def create_item(item: Item):
	inventory[len(inventory)+1] = item.dict()

Let’s add all this code to our main.py file and clean up our other routes to make them more uniform.

from fastapi import FastAPI
from pydantic import BaseModel
from inventory import inventory

class Item(BaseModel):
	flavor: str
	description: str
	price: float
	vegan: bool
	quantity: int

app = FastAPI()

@app.get("/items/")
def root():
	return inventory

@app.get("/items/{id}")
def get_item(id: int):
	return inventory[id]

@app.post("/items/")
def create_item(item: Item):
	inventory[len(inventory)+1] = item.dict()
	return inventory

The POST method is created using the same path as our GET method. This kind of route declaration and mapping of HTTP verbs to different path functions is a common technique used when building RESTful APIs. It provides a uniform interface that identify resources; in our case, items.

To test the POST method, visit your application’s docs page at http://localhost:8000/docs/. FastAPI comes with automatically generated documentation in the form of a web user interface.

Swagger UI interface

Whenever you create a new route, FastAPI will include it on your docs page and color-code it based on the HTTP method request type. It also lists each path, parameters, and the function that gets called when the path is accessed.

Open the drop down for GET /items/ Root and you will see the following.

Get Items path

All of the routes on your docs page contain two sections: Parameters and Responses. The Parameters section will list any path parameters that the route accepts. When you test routes containing parameters, you can enter them here. The Responses section contains a list of possible HTTP response codes. It will also update with any response messages that are returned when testing routes.

Click the “Try it out” button. Then click the “Execute” button to test the route. This sends a GET request to our /items/ path. The root() function is used to return our entire inventory.

GET request

Next, scroll down to the POST method and test that route. When you click “Try it out” you get to modify the request body to add a new inventory item for Bubble Gum.

POST request

Modify the attributes as you like. Then click the Execute button. The new flavor now appears as the sixth element.

POST request result

PUT Request

The next request method we can add to our project is a PUT request. This is like a POST but instead of adding new items to the server PUT requests are used to update existing data.

Add the following code to the bottom of your main.py file.

@app.put("/items/{id}/")
def update_item(id: int, item: Item):
	inventory[id] = item.dict()
	return inventory

The path operation decorator, @app.put() uses the same /items/{id}/ route but now the path accepts PUT requests. The operation function update_item() receives the path parameter id so it knows which item to update in the inventory. The Item object contains the new data.

Head back to your /docs/ page and test the PUT route.

The PUT request method

Update the “Vanilla” flavor with a different set of values. Then check the result after you execute the request.

The PUT request result

DELETE Request

The DELETE request is almost the same as the GET and PUT methods, except we’ll be removing an item from the inventory.

Add the following to the bottom of your main.py file.

@app.delete("/items/{id}/")
def delete_item(id: int):
	del inventory[id]
	return inventory

Once again, head back to your /docs/ page and test the DELETE route and make sure the specified item gets removed.

The DELETE request method

Summary

In this post, we added CRUD (create, read, update, and delete) operations to our inventory project. And we explored the FastAPI-generated docs page and how to test our endpoints from within our web browser.

The source code from this lesson is located in the GitHub repository in the folder named 03-basic-routing.

In the next lesson on Documentation, we explore FastAPI’s /docs/ page in a little more detail and look at what alternatives there are for testing web APIs.