Python FastAPI Tutorial (Part 2): HTML Frontend for Your API - Jinja2 Templates
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.
Create a `templates/` directory and configure `Jinja2Templates(directory='templates')` so FastAPI can locate template files.
Briefing
FastAPI keeps its JSON API endpoints, but adds human-friendly HTML pages by wiring Jinja2 templates into new “page routes.” The key move is switching the home route from returning a raw HTML string to returning a `TemplateResponse` backed by real template files, then injecting dynamic data through a context dictionary—so the same posts data can power both the browser view and the JSON endpoint.
The tutorial starts from a working baseline: the home route returns a simple `<h1>` string, while `/api/posts` returns a JSON list of posts. To replace the brittle string-building approach, it creates a `templates/` directory (FastAPI convention) and configures the app with a `Jinja2Templates` instance pointing at that folder. A first template, `home.html`, is introduced with basic HTML structure. The home route is updated to accept a `request` parameter (required by Jinja2), and to return `templates.TemplateResponse(request,
Cornell Notes
FastAPI’s JSON endpoints stay intact, while new HTML page routes render the same data using Jinja2 templates. The workflow is: create a `templates/` directory, configure `Jinja2Templates`, update routes to return `TemplateResponse` (including a required `request` argument), and pass dynamic values via a context dictionary. Inside templates, Jinja2 syntax provides `for` loops to iterate over posts, `{{ ... }}` to display variables, and `{% if ... %}...{% else %}...{% endif %}` for conditionals like a dynamic page title. Template inheritance then eliminates duplicated HTML by moving shared layout into `layout.html` and letting child templates override a `content` block. Finally, static assets are served from a mounted `static/` directory and referenced with `url_for` for robust links.
Why does the home route need a `request` parameter when returning a Jinja2 template response?
How does Jinja2 render a list of posts inside `home.html`?
What problem does template inheritance solve, and how is it implemented here?
How are static files served, and why do `url_for` changes matter?
Why did the navigation link for “Home” unexpectedly go to `/post`, and how is it corrected?
Review Questions
- What three pieces are required to render a Jinja2 template response in FastAPI (route signature, template selection, and context data)?
- How do `{% block %}` and `{% extends %}` work together to reduce duplication across multiple pages?
- What’s the difference between using `url_for` for routes versus using it for static assets, and why is it safer than hardcoding paths?
Key Points
- 1
Create a `templates/` directory and configure `Jinja2Templates(directory='templates')` so FastAPI can locate template files.
- 2
Switch from returning raw HTML strings to returning `templates.TemplateResponse`, passing the required `request` plus the template name.
- 3
Inject dynamic values into templates via a context dictionary (e.g., `{'posts': posts}`) and render them with `{{ ... }}`.
- 4
Use Jinja2 control structures—`{% for ... %}` loops and `{% if ... %}` conditionals—to display lists and conditional content like page titles.
- 5
Use template inheritance with a parent `layout.html` and a `{% block content %}` section to avoid repeating headers, navigation, and footers.
- 6
Mount a `static/` directory with `app.mount('/static', StaticFiles(...))` and reference assets using `url_for('static', path='...')`.
- 7
Assign explicit route names when one function has multiple decorators, so `url_for` generates the correct URL (e.g., `name='home'` vs `name='post'`).