CS504070 - FastAPI Tutorials
  • 🧩CS504070 - FastAPI Tutorials
  • 🔎Unit 1: Python Type Hint
  • ⚙️Unit 2: Pydantic
  • 🔃Unit 3: Concurrency
  • 💾Unit 4: Install FastAPI
  • 🍉Unit 5: Hello World!
  • 🍌Unit 6: Path Parameters
  • 🍋Unit 7: Query Parameters
  • 🍊Unit 8: Request Body
  • 🍐Unit 9: Query Parameters and Validations
  • 🍎Unit 10: Path Parameters and Validations
  • 🍏Unit 11: Multiple Parameters
  • 🍇Unit 12: Request Body - List Fields and Nested Models
  • 🍓Unit 13: Data Types
  • 🍪Unit 14: Cookie Parameters
  • 🫐Unit 15: Header Parameters
  • 🍈Unit 16: Response Model - Return Type
  • 🍒Unit 17: Additional Models
  • 🥑Unit 18: Implementing JWT Authentication with FastAPI
  • ⚙️Appendix A
  • 🍭Appendix B
Powered by GitBook
On this page
  • Introduction
  • Query Parameters
  • Additional validation
  • Add more validations
  • Using regular expression
  • Default value
  • Query parameter as a list
  • Exclude from the generated OpenAPI schema
  • Summary

Unit 9: Query Parameters and Validations

Introduction

In FastAPI, query parameters and string validations are powerful features that allow developers to specify the type of data expected from the client in the URL's query string. FastAPI automatically handles validation, serialization, and documentation for these parameters.


Query Parameters

Query parameters are the key-value pairs appended to the URL after the ? symbol. In FastAPI, you define these parameters as function arguments in your path operation function. FastAPI automatically interprets them as query parameters.

Let's take a quick look of using query parameter in FastAPI:

from fastapi import FastAPI

app = FastAPI()

@app.get("/items/")
async def read_items(q: str = None):
    output = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        output.update({"q": q})
    return output

The read_items function can be accessible at http://127.0.0.1:8000/items/?q=update-items where update-item is value of q (query) parameter.

Additional validation

We're going to update the above program to validate the length of the given q parameter doesn't exceed 50 characters. To achieve this, we import:

  • Query from fastapi

  • Annotated from typing (or from typing_extensions in Python below 3.9)

from fastapi import FastAPI, Query
from typing import Annotated

app = FastAPI()

@app.get("/items/")
async def read_items():
    output = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        output.update({"q": q})
    return output

The statement q: Annotated[str | None, Query(max_length=50)] = None can be broken into three steps:

  1. Define the type hint as q: str | None

  2. Wrap the type hint with Annotated, so it becomes Annotated[str | None]

  3. Add Query to Annotated in the q parameter, it finally becomes q: Annotated[str | None, Query(max_length=50)] = None

Both of step (1) and (2) mean the same thing, q is a parameter that can be a str or None, and by default, it is None.

With the validation we just defined above, FastAPI can now:

  • Validate the data making sure that the max length is 50 characters

  • Show a clear error for the client when the data is not valid

  • Document the parameter in the OpenAPI schema path operation

In the above, if the given q parameter exceeds 50 characters, FastAPI will response an error like this:

{
    "detail": [
        {
            "type": "string_too_long",
            "loc": [
                "query",
                "q"
            ],
            "msg": "String should have at most 50 characters",
            "input": "update-items",
            "ctx": {
                "max_length": 5
            },
            "url": "https://errors.pydantic.dev/2.6/v/string_too_long"
        }
    ]
}

Add more validations

For example, we can add min_length to the Query, as follows:

from fastapi import FastAPI, Query
from typing import Annotated

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(min_length=5, max_length=50)] = None):
    output = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        output.update({"q": q})
    return output

Using regular expression

We can also take advantage of regular expression to validate given query parameters.

A regular expression (ReGex) is a sequence of characters that forms a search pattern, primarily used for string searching and manipulation. It can be used to find, match, or replace text in strings. Regular expressions provide a concise and flexible means to "match" (specify and recognize) strings of text, such as particular characters, words, or patterns of characters. They are commonly used in text processing and data validation tasks, allowing complex criteria for string matching to be defined with a short and expressive syntax.

from fastapi import FastAPI, Query
from typing import Annotated

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[str | None, Query(min_length=5, max_length=50, pattern="^FixedQuery$")] = None):
    output = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
    if q:
        output.update({"q": q})
    return output

This specific regular expression pattern checks that the received parameter value:

  • ^ starts with the following characters, doesn't have any characters before

  • FixedQuery has the exact value FixedQuery (case-sensitive)

  • $ ends there, doesn't have any more characters after FixedQuery

If a request q doesn't pass the regular expression validation, such as q=fixedquery, FastAPI will response an error like this:

{
    "detail": [
        {
            "type": "string_pattern_mismatch",
            "loc": [
                "query",
                "q"
            ],
            "msg": "String should match pattern '^FixedQuery$'",
            "input": "fixedquery",
            "ctx": {
                "pattern": "^FixedQuery$"
            },
            "url": "https://errors.pydantic.dev/2.6/v/string_pattern_mismatch"
        }
    ]
}

Default value

Instead of using None default value, you can use another value as a default value by changing the None value in the parameter definition statement, as follows:

q: Annotated[str | None, Query(min_length=5, max_length=50] = "defaultValue"

Query parameter as a list

You can obtain multiple values for a given q parameter by defining it as a list, as follows:

from fastapi import FastAPI, Query
from typing import Annotated

app = FastAPI()

@app.get("/items/")
async def read_items(q: Annotated[list[str] | None, Query()] = None):
    query_items = {"q": q}
    return query_items

Then, the API endpoint is:

http://localhost:8000/items/?q=foo&q=bar

And the reponse will be:

{
    "q": [
        "foo",
        "bar"
    ]
}

Exclude from the generated OpenAPI schema

To exclude a query parameter from the generated OpenAPI schema (and thus, from the automatic documentation systems), set the parameter include_in_schema of Query to False:

hidden_query: Annotated[str | None, Query(include_in_schema=False)] = None

Summary

FastAPI's query parameters and string validations are integral features that simplify the development of robust and well-documented APIs. By leveraging type annotations and FastAPI's Query class, you can easily validate incoming data, enforce business rules, and automatically generate OpenAPI documentation for your endpoints.

PreviousUnit 8: Request BodyNextUnit 10: Path Parameters and Validations

Last updated 1 year ago

🍐
Page cover image