Insights

Understanding Query Params, Headers, and Body Params in FastAPI: When to Use Each

Understanding Query Params, Headers, and Body Params in FastAPI: When to Use Each

Understanding Query Params, Headers, and Body Params in FastAPI: When to Use Each

Saurabh Jain

Sep 11, 2024

When building an API, you’ll often need to send data to the server. In FastAPI, there are three main ways to do this: through query parameters, headers, and body parameters. Each has its own purpose and should be used in the right context to ensure clarity and security.

In this blog, we'll explore the differences between these types of parameters, how to use each in a FastAPI application, and when it's appropriate to use them.

1. Query Parameters

What Are Query Parameters?

Query parameters are part of the URL, and they appear after the ? symbol in the URL string. They are typically used to filter, modify, or specify optional data for an endpoint. Query parameters are passed as key-value pairs, separated by an &. These are a part of the query string of a URL.

Query parameters in FastAPI are declared as function parameters in the endpoint functions. FastAPI automatically detects them, providing automatic validation and type checking.

When to Use Query Parameters?

  • When you need to send optional or additional data without modifying the core operation of an endpoint.

  • To provide filtering or pagination functionality.

  • When data should be visible and easily changeable via the URL.

Optional query parameters can also be declared by providing a default value in FastAPI.

FastAPI Example

from fastapi import FastAPI

app = FastAPI()

@app.get("/items/")
async def read_items(skip: int = 0, limit: int = 10):
    return {"skip": skip, "limit": limit}

# Example request: /items/?skip=5&limit=20

In this example, skip and limit are optional query parameters for pagination. They are part of the query string, and FastAPI assigns default values when they are not provided.

2. Headers

What Are Headers?

HTTP headers are sent with every HTTP request and response, containing metadata about the communication. Request headers carry information like content type, user-agent, and authorization tokens. Unlike query parameters, headers are not part of the URL but are instead transmitted as part of the HTTP request.

In FastAPI, you can access request headers by using the Header class in your route functions.

When to Use Headers?

  • When sending sensitive information like authentication tokens (e.g., API keys or JWTs).

  • For passing metadata, like content types and caching behavior.

  • To control content negotiation or authorization.

FastAPI Example

from fastapi import FastAPI, Header

app = FastAPI()

@app.get("/users/me")
async def get_user(authorization: str = Header(None)):
    if authorization:
        return {"Authorization": authorization}
    return {"message": "No Authorization header provided"}

# Example request: GET /users/me with header `Authorization: Bearer <token>`

In this example, the authorization HTTP header is passed to authenticate the user. If the request headers don’t contain authorization information, the API responds with an error.

3. Body Parameters

What Are Body Parameters?

Body parameters are sent in the request body, typically in JSON format. These are used when sending more complex data, such as creating or updating a resource. Body parameters in FastAPI are particularly useful for structured data, such as objects or arrays, and are defined using Pydantic models, which ensure validation and type-checking.

When to Use Body Parameters?

  • When you need to send complex or large datasets that shouldn’t be exposed in the URL or headers.

  • To send private data.

  • For creating or updating resources.

FastAPI Example

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    name: str
    description: str = None
    price: float
    tax: float = None

@app.post("/items/")
async def create_item(item: Item):
    return {"item": item}

# Example request: POST /items/ with JSON body:
# {
#   "name": "Laptop",
#   "description": "A high-end laptop",
#   "price": 1500.0,
#   "tax": 150.0
# }

Here, the body data is sent as a JSON object in the request. The Item model ensures that data is validated according to its schema.

Path Parameters

What Are Path Parameters?

Path parameters are part of the URL path itself. They are used when an endpoint needs to operate on a specific resource. In FastAPI, you declare path parameters by enclosing the name of the parameter in curly braces ({}) in the endpoint path.

When to Use Path Parameters?

  • When the endpoint needs to work with a specific resource (e.g., fetching a specific item by ID).

  • When the identifier is part of the resource itself.

FastAPI Example

from fastapi import FastAPI

app = FastAPI()

@app.get("/items/{item_id}")
async def read_item(item_id: int):
    return {"item_id": item_id}

# Example request: GET /items/123

In this case, item_id is a path parameter, and it is required in the URL for the endpoint to function properly.

Choosing the Right Parameter Type

Here are some guidelines to help decide when to use each type of parameter:

  • Query Parameters: Best for optional data like filtering, sorting, or pagination. They are part of the query string and can be used to modify the behavior of an endpoint without affecting its core operation.

  • Headers: Use request headers when sending metadata like authentication tokens or content types. Headers keep this information hidden from the URL.

  • Body Parameters: Ideal for large, structured data, such as creating or updating resources.

  • Path Parameters: Use path parameters when referring to a specific resource by ID or another identifier. These parameters are part of the URL path and are required.

Combining Parameters in FastAPI

FastAPI allows combining multiple types of parameters in a single route. Let’s look at an example that combines path, query, and header parameters:

from fastapi import FastAPI, Header
from pydantic import BaseModel

app = FastAPI()

class Item(BaseModel):
    name: str
    price: float

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, authorization: str = Header(None), discount: float = 0):
    if not authorization:
        return {"error": "Unauthorized"}
    return {"item_id": item_id, "item": item, "authorization": authorization, "discount": discount}

# Example request: PUT /items/123?discount=10 with JSON body:
# {
#   "name": "Laptop",
#   "price": 1400.0
# }
# and header `Authorization: Bearer <token>`

This example combines a path parameter (item_id), an HTTP header (authorization), and an optional query parameter (discount) in a single request.

Conclusion

Understanding the difference between query parameters, path parameters, request headers, and body parameters is essential when building APIs in FastAPI. Each type serves a specific purpose, whether it’s passing optional data, sending sensitive information securely, or creating resources. Using the right parameter type for each situation will make your FastAPI application more effective, secure, and easier to use.

  • Query Parameters: Use for optional filters or pagination.

  • Headers: Ideal for sensitive data like tokens and metadata.

  • Body Parameters: Best for large or complex datasets.

  • Path Parameters: Use when you need to operate on specific resources.

By following these best practices, you can ensure that your API is designed for efficiency and clarity.

Saurabh Jain

Share this post