Python FastAPI Tutorial (Part 3): Path Parameters - Validation and Error Handling
Based on Corey Schafer's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.
Define a single-resource endpoint with a path parameter like /api/post/{post_id} and capture it in the handler signature.
Briefing
FastAPI path parameters let a single URL target one specific resource—while type hints automatically enforce validation and generate clear client-facing errors. The tutorial builds a “get one post” endpoint at /api/post/{post_id} by capturing post_id from the URL, typing it as an integer, and searching the in-memory list of posts for a matching ID. When a match exists, the endpoint returns that post as JSON; when it doesn’t, it must return a proper HTTP 404 so clients can reliably distinguish “success” from “not found.”
A key correction comes from observing that returning a plain error object still produces an HTTP 200 response, which misleads API consumers. The fix is to raise FastAPI’s HTTPException with status code 404 and a detail message like “post not found.” After that change, requests for missing IDs (e.g., /api/post/99) correctly return a 404 response while existing IDs (e.g., /api/post/1 and /api/post/2) return 200.
Type validation is then tested by sending a non-integer value such as /api/post/hello. Because post_id is annotated as int, FastAPI rejects the request before the handler runs, returning a 422 Unprocessable Entity with a detailed error explaining that the input could not be parsed as an integer. This validation also appears in the auto-generated API docs, where the route shows post_id as an integer and the “Try it out” interface blocks invalid inputs.
The tutorial then mirrors the API behavior for browser users by adding an HTML route for /post/{post_id}. A new Jinja2 template (post.html) displays a single post’s author image, metadata, title, and content. The route returns TemplateResponse with post.html and passes the found post into the template context. Links on the homepage are updated using url_for so each post title becomes a clickable link to /post/{post_id}, avoiding hard-coded URLs and keeping routing changes centralized.
Error handling becomes more nuanced once both API and HTML routes exist. Returning JSON errors from HTML pages is confusing for human users, so the app adds exception handlers that choose the response format based on the request path. For missing pages and missing posts, Starlette’s HTTP exceptions and FastAPI’s HTTPException are caught; API paths (starting with /api) return JSON with the correct status code, while non-API paths render an error.html template with the status code and message.
Validation errors require separate handling. RequestValidationError is caught with another exception handler: API routes return JSON containing the full validation details with a 422 status, while HTML routes show a simpler “request was invalid” message without exposing technical internals. The result is a clean separation of concerns: API endpoints under /api consistently return JSON for programmatic clients, while template routes return HTML for browser users—both backed by the same in-memory post data, ready to be swapped for Pydantic schemas and a real database in later steps.
Cornell Notes
The tutorial adds a single-post endpoint using FastAPI path parameters: /api/post/{post_id}. Capturing post_id from the URL and typing it as int triggers automatic validation; invalid inputs like “hello” produce a 422 error, while missing IDs produce a 404 via HTTPException. It then creates a matching HTML route /post/{post_id} that renders a Jinja2 template (post.html) and updates homepage links using url_for. Finally, it implements exception handlers that return JSON for API routes and HTML error pages for browser routes, including separate handling for RequestValidationError so 422s behave correctly in both contexts.
How does FastAPI turn a URL segment into a function argument, and why does the type hint matter?
Why is raising HTTPException with a 404 status code important for missing posts?
What’s the difference between handling HTTPException and RequestValidationError in this setup?
How does the tutorial prevent JSON error responses from showing up on HTML pages?
How are homepage post links generated so they stay correct if routes change?
Review Questions
- What response status code should be returned when a post ID doesn’t exist, and how is it implemented in the handler?
- Why does /api/post/hello return 422 instead of reaching the post lookup loop?
- How do the exception handlers decide whether to return JSON or an HTML template for errors?
Key Points
- 1
Define a single-resource endpoint with a path parameter like /api/post/{post_id} and capture it in the handler signature.
- 2
Type annotate path parameters (e.g., post_id: int) to get automatic validation and 422 errors for invalid inputs.
- 3
Return correct REST status codes by raising HTTPException for missing resources instead of returning error dictionaries.
- 4
Create a parallel HTML route (e.g., /post/{post_id}) that renders a Jinja2 template and passes the matched post into the template context.
- 5
Use url_for in templates to generate links to post pages, passing path parameters as keyword arguments.
- 6
Implement exception handlers that return JSON for API routes and HTML error pages for non-API routes.
- 7
Handle RequestValidationError separately so API clients receive detailed 422 validation output while HTML users get a simpler message.