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

The Ultimate FastAPI Tutorial Part 2 - URL Path Parameters

In part 2 of the FastAPI tutorial, we'll look at an API endpoint with path parameters
Created: 16 July 2021
Last updated: 16 July 2021


Welcome to the Ultimate FastAPI tutorial series. This post is part 2. 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

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 - An Endpoint With Query Params
Using Basic Type Hints in FastAPI

FastAPI logo

Practical Section - An Endpoint With Path Params

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
# skipping...

# 1
        "id": 1,
        "label": "Chicken Vesuvio",
        "source": "Serious Eats",
        "url": "",
        "id": 2,
        "label": "Chicken Paprikash",
        "source": "No Recipes",
        "url": "",
        "id": 3,
        "label": "Cauliflower and Tofu Curry Recipe",
        "source": "Serious Eats",
        "url": "",

# skipping ahead ...

# 2 - New addition, path parameter
@api_router.get("/recipe/{recipe_id}", status_code=200)
def fetch_recipe(*, recipe_id: int) -> dict:  # 3
    Fetch a single recipe by ID

    # 4
    result = [recipe for recipe in RECIPES if recipe["id"] == recipe_id]
    if result:
        return result[0]

# ...

Let’s break this down:

  1. We’ve created some example recipe data in the RECIPE list of dictionaries. For now, this is basic and minimal but serves our purposes for learning. Later in the tutorial series we will expand this dataset and store it in a database.
  2. We’ve created a new GET endpoint /recipe/{recipe_id}. Here the curly braces indicate the parameter value, which needs to match one of the arguments taken by the endpoint function fetch_recipe.
  3. The fetch_recipe function defines the logic for the new endpoint. The type hints for the function arguments which match the URL path parameters are used by FastAPI to perform automatic validation and conversion. We’ll look at this in action in a moment.
  4. We simulate fetching data by ID from a database with a simple list comprehension with an ID conditional check. The data is then serialized and returned as JSON by FastAPI.

Having done all that (and followed the README setup instructions), you can run the code in the example repo with this command (be sure to cd into the part 2 folder): poetry run ./

Navigate to localhost:8001/docs

Give the endpoint a try:

  • Expand the GET endpoint by clicking on it
  • Click on the “Try It Out” button
  • Enter the value “1” for the recipe_id
  • Press the large “Execute” button
  • Press the smaller “Execute” button that appears

FastAPI Query Param Example

You should see the Chicken Vesuvio recipe in the response.

Using Basic Type Hints in FastAPI

Let’s add a print statement to further understand what’s happening in the endpoint:

@api_router.get("/recipe/{recipe_id}", status_code=200)
def fetch_recipe(*, recipe_id: int) -> dict:
    Fetch a single recipe by ID
    print(type(recipe_id))  # ADDED

    result = [recipe for recipe in RECIPES if recipe["id"] == recipe_id]
    if result:
        return result[0]

Now when you try out the endpoint, you’ll see in your terminal:

FastAPI Query Param Example

Now change the type hint to a string:

def fetch_recipe(*, recipe_id: str) -> dict:
# skipping...

Now in your terminal, when you call the endpoint you’ll see a string being printed.

This is because FastAPI is coercing the input parameter type based on the function argument type hints. This is a handy way of preventing input errors. You’ll notice that after you have changed the recipe_id to a string, you no longer get responses to your API calls.

Quiz: Why do you think this is?

Stop reading and try and figure it out without reading below

OK, here is the answer

Because the recipe_id is a string, the == match in the list comprehension is no longer matching the integer ID values in the RECIPES list of dictionaries. This is a very simple example of how FastAPI’s integration with type hints can prevent many input mistakes, without us having to write additional checks in the code (less code means fewer bugs).

We’re just scratching the surface of how FastAPI uses type hints, more on this in the next couple of parts of the tutorial.

Further Reading - Python Type Declarations

Continue Learning FastAPI

In the next part of the tutorial, we’ll shift from path parameters to query parameters.

Go to part 3

If you’re interested in dealing with POST requests (where your request body is validated) that’s covered in part 4