FastAPI Basics • Lesson 7

Body - Multiple Parameters

Master all aspects of FastAPI's Body - Multiple Parameters following the complete official tutorial. Learn to mix parameters, use multiple body params, singular values, and more.

🎯 What You'll Learn

  • Mix path, query, and body parameters in one endpoint
  • Declare multiple body parameters using Pydantic models
  • Add singular values to request body using Body()
  • Combine multiple body parameters with query parameters
  • Embed single body parameters in JSON objects

Body - Multiple Parameters

🎯 What You'll Learn

This lesson covers the complete official FastAPI "Body - Multiple Parameters" tutorial. You'll master all the flexible ways FastAPI handles body parameters, from basic multiple models to advanced parameter mixing.

By the end of this lesson, you'll understand how to:

  • Mix path, query, and body parameters in one endpoint
  • Declare multiple body parameters using Pydantic models
  • Add singular values to request body using Body()
  • Combine multiple body parameters with query parameters
  • Embed single body parameters in JSON objects

📚 1. Mix Path, Query and Body Parameters

FastAPI allows you to freely mix different parameter types. You can make body parameters optional by setting default to None:

from typing import Union
from fastapi import FastAPI, Path
from pydantic import BaseModel

class Item(BaseModel):
    name: str
    description: Union[str, None] = None
    price: float
    tax: Union[float, None] = None

app = FastAPI()

@app.put("/items/{item_id}/basic")
async def update_item_basic(
    *,
    item_id: int = Path(title="The ID of the item to get", ge=0, le=1000),
    q: Union[str, None] = None,
    item: Union[Item, None] = None,
):
    results = {"item_id": item_id}
    if q:
        results.update({"q": q})
    if item:
        results.update({"item": item})
    return results

Request examples:

  • PUT /items/42/basic (no query, no body)
  • PUT /items/42/basic?q=search (with query, no body)
  • PUT /items/42/basic with JSON body (no query, with body)

🎯 2. Multiple Body Parameters

When you declare multiple Pydantic models as parameters, FastAPI expects a JSON body with keys for each model:

class User(BaseModel):
    username: str
    full_name: Union[str, None] = None

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, user: User):
    results = {"item_id": item_id, "item": item, "user": user}
    return results

Expected request body:

{
    "item": {
        "name": "Foo",
        "description": "The pretender",
        "price": 42.0,
        "tax": 3.2
    },
    "user": {
        "username": "dave",
        "full_name": "Dave Grohl"
    }
}

FastAPI automatically converts the request so item receives its content and user receives its content.

💡 3. Singular Values in Body

Use Body() to add singular values (not Pydantic models) to the request body:

from fastapi import Body

@app.put("/items/{item_id}/importance")
async def update_item_importance(
    item_id: int, 
    item: Item, 
    user: User, 
    importance: int = Body()
):
    results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
    return results

Expected request body:

{
    "item": {
        "name": "Foo",
        "description": "The pretender", 
        "price": 42.0,
        "tax": 3.2
    },
    "user": {
        "username": "dave",
        "full_name": "Dave Grohl"
    },
    "importance": 5
}

Without Body(), FastAPI would treat importance as a query parameter.

🔄 4. Multiple Body Parameters and Query

You can combine body parameters with query parameters. Singular values without Body() become query parameters:

@app.put("/items/{item_id}/full")
async def update_item_full(
    *,
    item_id: int,
    item: Item,
    user: User,
    importance: int = Body(gt=0),
    q: Union[str, None] = None,
):
    results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
    if q:
        results.update({"q": q})
    return results

URL: PUT /items/42/full?q=search

Request body:

{
    "item": {"name": "Foo", "price": 42.0},
    "user": {"username": "dave"},
    "importance": 5
}

📦 5. Embed Single Body Parameter

For a single body parameter, FastAPI normally expects the model content directly. Use Body(embed=True) to wrap it in a JSON object:

@app.put("/items/{item_id}/embed")
async def update_item_embed(item_id: int, item: Item = Body(embed=True)):
    results = {"item_id": item_id, "item": item}
    return results

With embed=True, expects:

{
    "item": {
        "name": "Foo",
        "description": "The pretender",
        "price": 42.0,
        "tax": 3.2
    }
}

Without embed=True, would expect:

{
    "name": "Foo",
    "description": "The pretender", 
    "price": 42.0,
    "tax": 3.2
}

🌟 Key Concepts

Parameter Detection Rules

  1. Path parameters: Declared in URL path with {parameter_name}
  2. Query parameters: Singular values with defaults, not in path
  3. Body parameters: Pydantic models or values with Body()

Request Body Structure

  • Single Pydantic model: Direct model content (unless embed=True)
  • Multiple Pydantic models: JSON object with model names as keys
  • Mixed with Body(): Singular values added as additional keys

Validation Benefits

  • All parameters validated according to their types
  • Pydantic models provide rich validation and serialization
  • Body() supports validation constraints (e.g., gt=0)
  • Automatic OpenAPI documentation generation

💡 Best Practices

Import Organization

from typing import Union
from fastapi import FastAPI, Path, Body
from pydantic import BaseModel

Parameter Order

@app.put("/items/{item_id}/example")
async def example(
    *,  # Force keyword-only arguments
    item_id: int,  # Path parameter
    item: Item,    # Body parameter (Pydantic model)
    user: User,    # Body parameter (Pydantic model)
    importance: int = Body(gt=0),  # Body parameter (singular)
    q: Union[str, None] = None,    # Query parameter
):
    pass

Model Design

  • Use Union[Type, None] for optional fields
  • Provide sensible defaults for optional parameters
  • Add validation constraints where appropriate
  • Use descriptive field names and model names

🔗 What's Next?

In the next lesson, you'll learn about Body - Fields, where you'll discover how to add validation, metadata, and examples directly to Pydantic model fields using the Field() function.

📖 Additional Resources

💡 Hint

FastAPI provides flexible ways to handle body parameters: multiple models create nested JSON, Body() adds singular values, and embed=True wraps single models.

Ready to Practice?

Now that you understand the theory, let's put it into practice with hands-on coding!

Start Interactive Lesson