Fundamentals
Path Parameters and Query Parameters
Path parameters and query parameters are the primary mechanisms for dynamic data exchange between a client and a FastAPI server. Mastering these allows developers to create flexible, resource-oriented APIs that handle specific identifiers and optional filtering seamlessly. You should reach for them whenever your endpoints need to process variable user input, whether it is locating a unique database record or applying constraints to a list of results.
The Logic of Path Parameters
Path parameters are essential when you need to identify a specific resource within your application. When you define a route with curly braces, like '/users/{user_id}', you are instructing the framework to treat that part of the URL as a variable. FastAPI uses the type hints provided in your function signature to automatically parse and validate the input. If a user provides an alphabetical string where an integer is expected, the framework rejects the request immediately with a clear error, preventing invalid data from ever reaching your business logic. This declarative approach enforces a strict contract between the client and your code. By leveraging standard Python types, you ensure that the parameter inside your function behaves exactly as expected, whether it is an integer, a float, or a custom UUID, allowing for safe, predictable operations on your resources.
from fastapi import FastAPI
app = FastAPI()
# The variable 'item_id' is captured from the URL path.
# The type hint 'int' ensures FastAPI validates the input as a number.
@app.get("/items/{item_id}")
def read_item(item_id: int):
return {"item_id": item_id} # FastAPI converts the string path to an intUnderstanding Query Parameters
Query parameters function differently from path parameters because they are not part of the URL path itself, but are appended to the URL after a question mark. They are ideal for optional data, filtering, sorting, or pagination logic. Because these parameters are defined as function arguments that are not part of the path, FastAPI automatically interprets any argument that does not match a path variable as a query parameter. This design allows for highly flexible endpoints where a client can choose to provide zero, some, or all available filters. If you provide a default value, the parameter becomes optional; if you omit the default, the parameter becomes mandatory. This mechanism provides a clean way to manage API complexity, ensuring that clients can request only the data they need without changing the fundamental structure of the endpoint path.
from fastapi import FastAPI
app = FastAPI()
# 'skip' and 'limit' are query parameters.
# They appear as ?skip=0&limit=10 in the request URL.
@app.get("/items/")
def read_items(skip: int = 0, limit: int = 10):
# Return a simulated list based on query parameters
return {"data": f"Items from {skip} to {skip + limit}"}Required versus Optional Parameters
A critical aspect of API design is distinguishing between what is necessary to perform a task and what is merely helpful. In FastAPI, the distinction between required and optional parameters is handled through the presence of default values. When you define a path parameter, it is inherently required because the URL structure would be broken without it. However, query parameters are much more flexible. By setting a default value in the function signature, you explicitly declare that a parameter is optional. If the client does not provide it, the framework injects the default value into your function. This pattern allows you to write robust code that gracefully handles missing client data without needing verbose conditional checks. By strategically choosing your defaults, you create an API that is both easy to use for beginners and powerful for advanced power users.
from fastapi import FastAPI
app = FastAPI()
# 'q' is an optional query parameter with a default value of None.
@app.get("/search/")
def search_items(q: str = None):
if q:
return {"search_query": q}
return {"message": "No search term provided"}Data Validation and Conversion
FastAPI does more than just capture input; it performs heavy lifting regarding data validation and type conversion. When a request arrives, the framework inspects the incoming data and attempts to match it against your specified type hints. If the incoming value cannot be converted, such as passing text into an integer field, FastAPI generates a high-quality, JSON-formatted error message automatically. This is a massive time-saver, as it removes the need to write repetitive validation logic manually for every single endpoint. Because the conversion happens before your function code executes, you can work with your data as standard Python objects. This seamless integration ensures that you can focus on your business requirements rather than spending cycles on input parsing, error handling, and manual validation routines that are prone to bugs.
from fastapi import FastAPI
app = FastAPI()
# FastAPI ensures 'price' is a float and 'in_stock' is a boolean.
# If the user sends a string like 'abc', they get an automated 422 error.
@app.get("/products/{product_id}")
def get_product(product_id: int, price: float, in_stock: bool = True):
return {"id": product_id, "price": price, "available": in_stock}Combining Path and Query Parameters
You will frequently encounter scenarios where an API endpoint needs both specific resource identification and configuration options. Combining path and query parameters is straightforward: simply declare your path variables within the route string and define your query parameters as additional arguments in the function signature. FastAPI is intelligent enough to parse the path segments for the identifiers and look at the request URL for the query strings. This separation of concerns keeps your code readable and your URL structure intuitive. A well-designed endpoint often uses the path to define 'what' resource is being accessed and query parameters to define 'how' the response should be formatted. By organizing your API this way, you create a self-documenting interface that feels natural to users and remains easy to maintain as your project requirements grow in complexity.
from fastapi import FastAPI
app = FastAPI()
# Path parameter: user_id
# Query parameter: active_only
@app.get("/users/{user_id}/orders")
def get_user_orders(user_id: int, active_only: bool = False):
return {"user": user_id, "only_active": active_only}Key points
- Path parameters are defined in the route string using curly braces to identify specific resources.
- Query parameters are defined as function arguments that are not present in the route path.
- Type hints are mandatory for FastAPI to perform automatic validation and data conversion.
- Default values in function arguments make query parameters optional for the client.
- FastAPI automatically handles errors when client input does not match the defined type hints.
- Path parameters are considered required components of a specific resource location.
- You can combine multiple path and query parameters to create sophisticated API endpoints.
- The framework provides automatic documentation generation based on the parameter definitions you supply.
Common mistakes
- Mistake: Defining a parameter in the path operation function without declaring it in the path string. Why it's wrong: FastAPI cannot map the argument to a path segment if it's not present in the URL pattern. Fix: Ensure the variable name inside the curly braces matches the function argument exactly.
- Mistake: Misunderstanding the order of path parameters. Why it's wrong: Parameters are extracted by name, not position in the function signature, but confusion arises when naming clashes occur. Fix: Ensure the path string clearly defines the structure, e.g., /items/{item_id}.
- Mistake: Treating all function parameters as query parameters by default. Why it's wrong: FastAPI automatically interprets parameters not found in the path as query parameters, which can lead to unexpected API behavior if you intended them to be required path segments. Fix: Explicitly define the URL path and check the Swagger UI to verify parameter classification.
- Mistake: Adding trailing slashes to paths when not intended. Why it's wrong: FastAPI treats /items and /items/ as distinct routes, which can cause 404 errors if the client expects a specific endpoint structure. Fix: Decide on a consistent naming convention and use the redirect_slashes configuration if necessary.
- Mistake: Confusing path parameters (for specific resources) with query parameters (for filtering/sorting). Why it's wrong: Using path parameters for optional filters makes the API URI rigid and hard to extend. Fix: Use path parameters for resource identification and query parameters for state or filtering criteria.
Interview questions
What is the fundamental difference between a path parameter and a query parameter in FastAPI?
In FastAPI, path parameters are parts of the URL path itself, usually defined using curly braces like '{item_id}', and they are essential for identifying a specific resource. Query parameters are the key-value pairs that come after the question mark in a URL, such as '?limit=10'. You use path parameters when the resource identity is mandatory to locate the object, whereas you use query parameters for optional filtering, sorting, or pagination of a collection.
How do you define a path parameter in a FastAPI route, and how does the framework know which type to expect?
To define a path parameter, you include a placeholder in the route decorator path string, such as @app.get('/users/{user_id}'). FastAPI uses Python type hints in your function signature to perform automatic validation. If you define 'user_id: int', FastAPI will automatically convert the incoming string from the URL into an integer. If the conversion fails, FastAPI returns a clear 422 Unprocessable Entity error to the client automatically.
Why would you choose to use a query parameter instead of a path parameter for an endpoint that retrieves a list of items?
Query parameters are superior for listing resources because they are naturally optional and flexible. When retrieving a list, you rarely need every single item at once, so query parameters allow you to implement pagination (like 'page' or 'size') or filtering (like 'category' or 'status') without changing the URL structure. By declaring them with default values in your function arguments, FastAPI treats them as optional, making your API more robust and easier for clients to consume dynamically.
Can you explain how to set default values for query parameters and why this is a recommended practice in FastAPI?
Setting default values for query parameters is straightforward; you simply assign a value in the function signature, such as 'limit: int = 100'. This is a best practice because it defines the API's behavior when a client does not provide specific criteria. It prevents your application from crashing due to missing data while simultaneously documenting the default state of your API through the generated OpenAPI schema, which helps frontend developers understand expected inputs.
How do you handle required query parameters versus optional ones, and what is the role of the 'Query' class?
In FastAPI, a query parameter is optional if you provide a default value, like 'q: str = None'. If you omit the default value, FastAPI treats the parameter as strictly required. However, using the 'Query' class, such as 'q: str = Query(..., min_length=3)', allows you to explicitly mark a parameter as required while adding extra metadata like validation rules or descriptions. This is critical for maintaining strict API contracts and providing detailed error messages to users.
Compare the use cases for path parameters and query parameters when designing a RESTful API in FastAPI: when is one strictly better than the other?
Path parameters are strictly better for hierarchical resource identification, such as '/books/{book_id}/authors/{author_id}', because they imply a direct relationship and ownership of the resource. Conversely, query parameters are strictly better for non-hierarchical, secondary operations like sorting, searching, or filtering, such as '/books?sort=author&published=true'. If you were to use path parameters for filtering, your URL structure would become chaotic and unmanageable. Always use path parameters to point to a 'noun' and query parameters to specify the 'view' or 'subset' of that noun.
Check yourself
1. You have the path '/users/{user_id}'. If a user requests '/users/123', how does FastAPI treat the variable 'user_id'?
- A.As a query parameter because it is a numeric ID
- B.As a path parameter because it is specified in the URL path string
- C.As a request body parameter because it is a dynamic value
- D.As a header parameter because it is part of the URL
Show answer
B. As a path parameter because it is specified in the URL path string
Option 2 is correct because FastAPI parses variables enclosed in curly braces in the path string as path parameters. Option 1 is wrong because path parameters are determined by the URL template, not data type. Option 3 is wrong because path parameters are never passed in the body. Option 4 is wrong because URL segments are not headers.
2. Which of the following best describes when you should use a query parameter instead of a path parameter?
- A.When the parameter identifies a unique, mandatory resource
- B.When the parameter is a required path segment defined in the OpenAPI spec
- C.When the parameter is used for optional filtering, sorting, or pagination
- D.When the parameter contains sensitive information that should be hidden from the URL
Show answer
C. When the parameter is used for optional filtering, sorting, or pagination
Option 3 is correct because query parameters are designed for optional modifiers. Option 1 and 2 describe path parameters. Option 4 is incorrect because neither path nor query parameters should contain sensitive information.
3. Given the function: 'async def get_items(category: str, limit: int = 10):', if the user calls '/items?category=books', what will happen?
- A.The request will fail because 'limit' is missing
- B.The request will succeed, with 'category' set to 'books' and 'limit' to 10
- C.The request will succeed, with 'category' set to 'books' and 'limit' set to None
- D.The request will fail because 'category' is a path parameter
Show answer
B. The request will succeed, with 'category' set to 'books' and 'limit' to 10
Option 2 is correct because FastAPI detects 'category' and 'limit' as query parameters since they are not in the path string; 'limit' takes its default value. Option 1 is wrong because 10 is a default value. Option 3 is wrong because the default is 10, not None. Option 4 is wrong because there is no path string defined to make it a path parameter.
4. How does FastAPI handle a case where a path parameter name matches a query parameter name?
- A.It throws a startup error because parameter names must be unique
- B.It uses the query parameter value for both
- C.It prioritizes the path parameter, and the query parameter is ignored
- D.It allows both, but they are treated as distinct; the path parameter is strictly from the URL path
Show answer
D. It allows both, but they are treated as distinct; the path parameter is strictly from the URL path
Option 4 is correct because path parameters are scoped to the URL template, while query parameters are pulled from the URL query string. Option 1 is wrong because they are distinct. Option 2 and 3 are wrong because FastAPI correctly separates them based on the request parsing logic.
5. What is the result of using a default value for a path parameter, such as '/items/{item_id=5}'?
- A.FastAPI will use 5 if the item_id segment is missing in the URL
- B.This is invalid syntax in FastAPI; path parameters must be provided
- C.It sets the parameter to 5 but allows it to be overridden by a query parameter
- D.It turns the path parameter into an optional query parameter
Show answer
B. This is invalid syntax in FastAPI; path parameters must be provided
Option 1 is correct because while standard HTTP patterns usually require path segments, FastAPI allows defaults for path parameters. Option 2 is wrong because this is supported. Option 3 and 4 are wrong because this does not affect query parameter parsing.