Get AI summaries of any video or article — Sign up free
Python FastAPI Tutorial (Part 3): Path Parameters - Validation and Error Handling thumbnail

Python FastAPI Tutorial (Part 3): Path Parameters - Validation and Error Handling

Corey Schafer·
4 min read

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.

TL;DR

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?

A route like /api/post/{post_id} declares post_id as a path parameter. FastAPI captures the value from the URL and passes it into the handler function. Annotating post_id as int makes FastAPI validate the input automatically: /api/post/hello fails before the handler logic runs and returns a 422 Unprocessable Entity with an error message about parsing the value as an integer.

Why is raising HTTPException with a 404 status code important for missing posts?

Returning an error dictionary from the handler still results in a 200 OK response, which incorrectly signals success to clients. Switching to raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="post not found") ensures the response status matches the REST meaning of “resource not found,” letting clients handle it properly.

What’s the difference between handling HTTPException and RequestValidationError in this setup?

HTTPException covers explicit errors raised by application logic (like missing posts) and can be caught with an exception handler for starlette HTTP exceptions and FastAPI HTTPException. RequestValidationError covers automatic validation failures (like non-integer path parameters). Because validation errors don’t provide a simple detail string and always correspond to 422, the handler must return structured validation details for API clients and a simpler message for HTML users.

How does the tutorial prevent JSON error responses from showing up on HTML pages?

It adds exception handlers that inspect the request URL path. If the path starts with /api, the handler returns a JSONResponse with the appropriate status code and message. Otherwise, it returns a TemplateResponse using error.html, passing the status code and a user-friendly message so browser users see an HTML error page instead of raw JSON.

How are homepage post links generated so they stay correct if routes change?

In home.html, each post title becomes a link using url_for("post page", post_id=post.get("id")). url_for builds the correct URL from the route name and parameters, so changing the URL pattern in main.py automatically updates all generated links without editing templates manually.

Review Questions

  1. What response status code should be returned when a post ID doesn’t exist, and how is it implemented in the handler?
  2. Why does /api/post/hello return 422 instead of reaching the post lookup loop?
  3. How do the exception handlers decide whether to return JSON or an HTML template for errors?

Key Points

  1. 1

    Define a single-resource endpoint with a path parameter like /api/post/{post_id} and capture it in the handler signature.

  2. 2

    Type annotate path parameters (e.g., post_id: int) to get automatic validation and 422 errors for invalid inputs.

  3. 3

    Return correct REST status codes by raising HTTPException for missing resources instead of returning error dictionaries.

  4. 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. 5

    Use url_for in templates to generate links to post pages, passing path parameters as keyword arguments.

  6. 6

    Implement exception handlers that return JSON for API routes and HTML error pages for non-API routes.

  7. 7

    Handle RequestValidationError separately so API clients receive detailed 422 validation output while HTML users get a simpler message.

Highlights

Raising HTTPException fixes a subtle bug where missing posts previously returned 200 OK instead of the correct 404 Not Found.
Typing post_id as int makes FastAPI automatically reject /api/post/hello with a 422 Unprocessable Entity and a detailed parsing error.
Exception handling is split by route type: /api paths return JSON, while browser paths render error.html templates.
RequestValidationError needs its own handler because it carries validation details (not a single detail string) and maps to 422 responses.

Topics

Mentioned