FastAPI Basics • Lesson 3

Query Parameters

Learn to handle query parameters in URLs. Create flexible endpoints that accept optional parameters for filtering, pagination, and customization.

🎯 What You'll Learn

  • Understand what query parameters are and how they work
  • Create endpoints that accept optional query parameters
  • Set default values for query parameters
  • Use type hints for automatic query parameter validation
  • Handle both path and query parameters together

Query Parameters

🎯 What You'll Learn

Query parameters are a powerful way to make your APIs flexible by accepting optional parameters in the URL. Following the official FastAPI tutorial, you'll learn how to handle URL parameters that come after the ? symbol.

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

  • Understand what query parameters are and how they work
  • Create endpoints that accept optional query parameters
  • Set default values for query parameters
  • Use type hints for automatic query parameter validation
  • Handle both path and query parameters together

🔍 What are Query Parameters?

Query parameters are the optional parts of a URL that come after the ? symbol. They're used to modify or filter the response without changing the core endpoint.

Real-World Examples

You see query parameters everywhere:

  • Google Search: google.com/search?q=fastapi&lang=en
  • YouTube: youtube.com/results?search_query=python
  • Online stores: store.com/products?category=electronics&sort=price
  • Pagination: api.com/users?page=2&limit=20

🔧 How Query Parameters Work

Basic Syntax

When you declare other function parameters that are not part of the path parameters, they are automatically interpreted as "query" parameters.

from fastapi import FastAPI

app = FastAPI()

fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]

@app.get("/items/")
def read_items(skip: int = 0, limit: int = 10):
    return fake_items_db[skip : skip + limit]

How it works:

  • /items/ → Uses defaults: skip=0, limit=10
  • /items/?skip=20 → Uses: skip=20, limit=10 (default)
  • /items/?skip=20&limit=10 → Uses: skip=20, limit=10

🎨 Query Parameter Features

Default Values

Query parameters can have default values, making them optional:

@app.get("/items/")
def read_items(skip: int = 0, limit: int = 10):
    return fake_items_db[skip : skip + limit]

Optional Parameters

You can make parameters truly optional by setting them to None:

@app.get("/items/{item_id}")
def read_item(item_id: str, q: str | None = None):
    if q:
        return {"item_id": item_id, "q": q}
    return {"item_id": item_id}

URL Examples:

  • /items/foo{"item_id": "foo"}
  • /items/foo?q=search{"item_id": "foo", "q": "search"}

Type Conversion

FastAPI automatically converts query parameters to the specified types:

@app.get("/items/")
def read_items(skip: int = 0, limit: int = 10, active: bool = True):
    return {
        "skip": skip,      # Converted to int
        "limit": limit,    # Converted to int  
        "active": active   # Converted to bool
    }

URL Examples:

  • /items/?skip=5&limit=20&active=false
  • /items/?active=1 (1 becomes True)
  • /items/?active=0 (0 becomes False)

💡 Official Tutorial Examples

Example 1: Simple Query Parameters

From the official tutorial:

fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]

@app.get("/items/")
def read_items(skip: int = 0, limit: int = 10):
    return fake_items_db[skip : skip + limit]

Test it:

  • /items/ → Returns first 10 items
  • /items/?skip=1 → Skips first item, returns next 10
  • /items/?limit=2 → Returns first 2 items

Example 2: Combining Path and Query Parameters

@app.get("/items/{item_id}")
def read_item(item_id: str, q: str | None = None):
    if q:
        return {"item_id": item_id, "q": q}
    return {"item_id": item_id}

Test it:

  • /items/foo{"item_id": "foo"}
  • /items/foo?q=search{"item_id": "foo", "q": "search"}

Example 3: Multiple Query Parameters

@app.get("/users/")
def read_users(skip: int = 0, limit: int = 10, active: bool = True):
    # In a real app, you'd filter from a database
    return {
        "skip": skip,
        "limit": limit, 
        "active_only": active,
        "message": f"Showing {limit} users, skipping {skip}, active: {active}"
    }

🌟 Advanced Query Parameter Features

Required Query Parameters

Make query parameters required by not providing a default value:

@app.get("/search/")
def search_items(q: str):  # Required - no default value
    return {"query": q, "results": []}

Multiple Types

@app.get("/products/")
def get_products(
    category: str = "all",
    min_price: float = 0.0,
    max_price: float = 1000.0,
    in_stock: bool = True
):
    return {
        "category": category,
        "price_range": [min_price, max_price],
        "in_stock_only": in_stock
    }

List Parameters

@app.get("/items/")
def read_items(tags: list[str] = []):
    return {"tags": tags}

URL: /items/?tags=python&tags=fastapi&tags=tutorial

✨ Best Practices

1. Use Meaningful Default Values

# ✅ Good - sensible defaults
@app.get("/items/")
def read_items(skip: int = 0, limit: int = 10):
    pass

# ❌ Avoid - unclear defaults
@app.get("/items/")
def read_items(skip: int = 999, limit: int = 1):
    pass

2. Use Type Hints

# ✅ Good - automatic validation
@app.get("/items/")
def read_items(skip: int = 0, limit: int = 10):
    pass

# ❌ Missing - no validation
@app.get("/items/")
def read_items(skip=0, limit=10):
    pass

3. Handle Optional Parameters Properly

# ✅ Good - explicit None handling
@app.get("/items/{item_id}")
def read_item(item_id: str, q: str | None = None):
    if q:
        return {"item_id": item_id, "q": q}
    return {"item_id": item_id}

🚫 Common Beginner Mistakes

Mistake 1: Forgetting Default Values

# ❌ Wrong - required parameters become mandatory
@app.get("/items/")
def read_items(skip: int, limit: int):  # Users must provide both
    pass

# ✅ Correct - optional with defaults
@app.get("/items/")
def read_items(skip: int = 0, limit: int = 10):
    pass

Mistake 2: Wrong Optional Syntax

# ❌ Wrong - old Python syntax
@app.get("/items/{item_id}")
def read_item(item_id: str, q: Optional[str] = None):
    pass

# ✅ Correct - modern Python syntax
@app.get("/items/{item_id}")
def read_item(item_id: str, q: str | None = None):
    pass

Mistake 3: Not Handling None Values

# ❌ Wrong - will crash if q is None
@app.get("/items/{item_id}")
def read_item(item_id: str, q: str | None = None):
    return {"item_id": item_id, "query_length": len(q)}  # Error if q is None

# ✅ Correct - check for None
@app.get("/items/{item_id}")
def read_item(item_id: str, q: str | None = None):
    if q:
        return {"item_id": item_id, "q": q}
    return {"item_id": item_id}

🎉 What's Next?

Congratulations! You now understand how to use query parameters to make your APIs flexible and user-friendly. This is essential for building real-world APIs that need filtering, pagination, and customization.

You've learned:

  • How query parameters work in URLs
  • Setting default values for optional parameters
  • Combining path and query parameters
  • Handling None values for optional parameters

Coming up next: Request Body - learning how to handle POST requests with data in the request body!

📖 Additional Resources

💡 Hint

Query parameters come after the ? in URLs like /items/?skip=0&limit=10. Define them as function parameters with default values!

Ready to Practice?

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

Start Interactive Lesson