Early bird orders for the course, "FastAPI for Busy Engineers" are available at a discount until April 27th.

The Ultimate FastAPI Tutorial Part 3 - Query Parameters

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


Welcome to the Ultimate FastAPI tutorial series. This post is part 3. 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 - An Endpoint With Query Params
Further Reading - The Typing Module

FastAPI logo

Practical Section - An Endpoint With Query Params

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

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

# skipping...

# 1
        "id": 1,
        "label": "Chicken Vesuvio",
        "source": "Serious Eats",
        "url": "http://www.seriouseats.com/recipes/2011/12/chicken-vesuvio-recipe.html",
        "id": 2,
        "label": "Chicken Paprikash",
        "source": "No Recipes",
        "url": "http://norecipes.com/recipe/chicken-paprikash/",
        "id": 3,
        "label": "Cauliflower and Tofu Curry Recipe",
        "source": "Serious Eats",
        "url": "http://www.seriouseats.com/recipes/2011/02/cauliflower-and-tofu-curry-recipe.html",

# 2 new addition, query parameter
@api_router.get("/search/", status_code=200)  # 3
def search_recipes(
    keyword: Optional[str] = None, max_results: Optional[int] = 10  # 4 & 5
) -> dict:
    Search for recipes based on label keyword
    if not keyword:
        # we use Python list slicing to limit results
        # based on the max_results query parameter
        return {"results": RECIPES[:max_results]}  # 6

    results = filter(lambda recipe: keyword.lower() in recipe["label"].lower(), RECIPES)  # 7
    return {"results": list(results)[:max_results]}

# skipping...

Let’s break this down:

  1. We have our toy dataset (later this will go into a database and be expanded)
  2. We’ve created a new GET endpoint /search/. Notice it has no path parameters, which we looked at in part 2
  3. The search_recipes function defines the logic for the new endpoint. Its arguments represent the query parameters to the endpoint. There are two arguments: keyword and max_results. This means that a (local) query with both of these query parameters might look like: http://localhost:8001/search/?keyword=chicken&max_results=2
  4. Notice that for each argument, we specify its type and default. Both are Optional which comes from the Python standard library typing module. FastAPI is able to use these native Python type declarations to understand that the parameter does not need to be set (if we wanted the parameters to be mandatory, we would omit the Optional)
  5. Both parameters also have a default, specified via the = sign, for example, the max_result query parameter default is 10. If these parameters are not specified in the request, the default value will be used.
  6. We implement some basic search functionality using Python list slicing to limit results
  7. We use the Python filter capability for a very basic keyword search on our toy dataset. After our search is complete the data is serialized to JSON by the framework.

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

Navigate to the swagger UI at 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 “chicken” for the keyword
  • Press the large “Execute” button
  • Press the smaller “Execute” button that appears

FastAPI Query Param Example

After pressing the execute button (which just makes a GET request via curl under the hood), you can scroll down to see the response from FastAPI:

FastAPI Query Param response

Notice that only the two chicken recipes were returned. Try again with the keyword cauliflower to see the “Cauliflower and Tofu Curry” recipe returned. Then try adjusting the max_results parameter.

Further Reading - The Typing Module

  • Since Python 3.5, the standard library has included the typing module
  • Type declarations can be checked with a tool called mypy. This is a really useful cheatsheet

Continue Learning FastAPI

In the next part of the tutorial, we’ll cover more advanced endpoint input and output validation with Pydantic models, as well as dealing with POST endpoints and request body data.

Go to part 4