The course: "FastAPI for Busy Engineers" is available if you prefer videos.

The Ultimate FastAPI Tutorial Part 5 - Basic Error Handling

In part 5 of the FastAPI tutorial, we'll look at basic error handling
Created: 16 July 2021
Last updated: 16 July 2021


Welcome to the Ultimate FastAPI tutorial series. This post is part 5. The series is a project-based tutorial where we will build a cooking recipe API. Each post gradually adds more complex functionality, showcasing the capabilities of FastAPI, ending with a realistic, production-ready API. The series is designed to be followed in order, but if you already know FastAPI you can jump to the relevant part.


Project github repo directory for this part of the tutorial

Tutorial Series Contents

Optional Preamble: FastAPI vs. Flask

Beginner Level Difficulty

Part 1: Hello World
Part 2: URL Path Parameters & Type Hints
Part 3: Query Parameters
Part 4: Pydantic Schemas & Data Validation
Part 5: Basic Error Handling
Part 6: Jinja Templates
Part 6b: Basic FastAPI App Deployment on Linode

Intermediate Level Difficulty

Part 7: Setting up a Database with SQLAlchemy and its ORM
Part 8: Production app structure and API versioning
Part 9: Creating High Performance Asynchronous Logic via async def and await
Part 10: Authentication via JWT
Part 11: Dependency Injection and FastAPI Depends
Part 12: Setting Up A React Frontend
Part 13: Using Docker, Uvicorn and Gunicorn to Deploy Our App to Heroku
Part 14: Using Docker and Uvicorn to Deploy Our App to IaaS (Coming soon)
Part 15: Exploring the Open Source Starlette Toolbox - GraphQL (Coming soon)
Part 16: Alternative Backend/Python Framework Comparisons (i.e. Django) (Coming soon)

Post Contents

Practical Section - Adding Basic Error Handling

FastAPI logo

Practical Section - Adding Basic Error Handling

If you haven’t already, go ahead and clone the example project repo See the README file for local setup.

In the app/ file, you will find the following new code:

from fastapi import FastAPI, APIRouter, Query, HTTPException  # 1
# skipping...

@api_router.get("/recipe/{recipe_id}", status_code=200, response_model=Recipe)
def fetch_recipe(*, recipe_id: int) -> Any:
    Fetch a single recipe by ID

    result = [recipe for recipe in RECIPES if recipe["id"] == recipe_id]
    if not result:
        # the exception is raised, not returned - you will get a validation
        # error otherwise.
        # 2
        raise HTTPException(
            status_code=404, detail=f"Recipe with ID {recipe_id} not found"

    return result[0]

# skipping...

Let’s break this down:

  1. We import the HTTPException from FastAPI
  2. Where no recipe is found, we raise an HTTPException passing in a status_code of 404, which indicates the requested resource has not been found. See list of HTTP status codes.. Notice that we raise the exception, we do not return it. Returning the exception causes a validation error.

Having done all that (and followed the README setup instructions), you can run the code in the example repo with this command: poetry run ./

Navigate to localhost:8001/docs

Give the endpoint a try:

  • Expand the GET /recipe/{recipe_id} endpoint by clicking on it
  • Click on the “Try It Out” button
  • Set the recipe_id to 1
  • Click “Execute”

You should get the “Chicken Vesuvio” response:

recipe response

However, if you were to set the recipe_id to a non-existent one, say 4, then you will now get a 404:

recipe response

Exercise: Try POSTing to the /recipe endpoint to create a recipe with the ID 4 and then retry your GET request (you should no longer get a 404). Remember that in our current app’s basic form, created entries won’t be persisted after you CTRL+C and restart the server.

Continue Learning FastAPI

In the next part of the tutorial, we’ll cover serving HTML pages with Jinja2 templates.

Go to part 6