FastAPI HTML Response
When developing web applications with FastAPI, you'll often need to return HTML content rather than JSON. FastAPI provides several methods to serve HTML responses, from simple strings to fully templated pages. This guide will walk you through the various approaches to returning HTML content from your FastAPI application.
Introduction to HTML Responses in FastAPI
While FastAPI excels at building APIs that return JSON data, sometimes you need to serve complete web pages directly. Whether you're building a hybrid application, creating documentation, or implementing a simple user interface without a separate frontend framework, HTML responses are essential.
FastAPI provides the HTMLResponse
class from the fastapi.responses
module specifically for this purpose. This class allows you to return HTML content with the correct MIME type (text/html
) so browsers can properly render your pages.
Basic HTML Responses
Using the HTMLResponse Class
The simplest way to return HTML content is by using the HTMLResponse
class:
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.get("/", response_class=HTMLResponse)
async def read_root():
html_content = """
<!DOCTYPE html>
<html>
<head>
<title>FastAPI HTML Example</title>
</head>
<body>
<h1>Hello from FastAPI!</h1>
<p>This is an HTML response</p>
</body>
</html>
"""
return html_content
When you visit the root URL of your application, the browser will display:
Hello from FastAPI!
This is an HTML response
The key parts of this code are:
- Importing
HTMLResponse
fromfastapi.responses
- Setting
response_class=HTMLResponse
in the route decorator - Returning a string containing valid HTML
Returning HTMLResponse Directly
You can also create and return an HTMLResponse
object explicitly:
@app.get("/alternative")
async def read_alternative():
html_content = """
<!DOCTYPE html>
<html>
<head>
<title>FastAPI HTML Example</title>
</head>
<body>
<h1>Alternative Method</h1>
<p>This is another way to return HTML</p>
</body>
</html>
"""
return HTMLResponse(content=html_content)
Both approaches achieve the same result, but the first one is more concise when you know all responses from an endpoint will be HTML.
Dynamic HTML Content
In real applications, you'll usually want to generate HTML content dynamically based on data or parameters.
Using Path Parameters
Here's how to incorporate path parameters into your HTML response:
@app.get("/users/{name}", response_class=HTMLResponse)
async def read_user(name: str):
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<title>User Profile</title>
</head>
<body>
<h1>Hello, {name}!</h1>
<p>Welcome to your profile page</p>
</body>
</html>
"""
return html_content
When you visit /users/John
, the page will display:
Hello, John!
Welcome to your profile page
Rendering HTML Based on Query Parameters
You can also use query parameters to dynamically generate content:
@app.get("/greeting", response_class=HTMLResponse)
async def read_greeting(name: str = "Guest", color: str = "black"):
html_content = f"""
<!DOCTYPE html>
<html>
<head>
<title>Greeting</title>
<style>
h1 {{ color: {color}; }}
</style>
</head>
<body>
<h1>Hello, {name}!</h1>
<p>Thanks for visiting our site</p>
</body>
</html>
"""
return html_content
Visiting /greeting?name=Alice&color=blue
will show "Hello, Alice!" in blue text.
Using Templates with FastAPI
For more complex HTML rendering, you'll want to use a templating engine. Jinja2 is one of the most popular choices for Python applications.
Setting Up Jinja2 Templates
First, install the required packages:
pip install jinja2
Then, configure your FastAPI application to use Jinja2:
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
import os
app = FastAPI()
# Set up templates directory
templates = Jinja2Templates(directory="templates")
@app.get("/template/{name}", response_class=HTMLResponse)
async def read_template(request: Request, name: str):
# Pass the request and a context dictionary to the template
return templates.TemplateResponse(
"user.html",
{"request": request, "name": name}
)
In this example, you need to create a templates
directory in your project and add a user.html
file:
<!DOCTYPE html>
<html>
<head>
<title>{{ name }}'s Page</title>
</head>
<body>
<h1>Hello, {{ name }}!</h1>
<p>This page was rendered using Jinja2 templates</p>
</body>
</html>
When using templates:
- Create a
Jinja2Templates
instance with your templates directory - Include the
request
parameter in your endpoint function - Return a
TemplateResponse
with the template name and a context dictionary
Serving Static Files with HTML
Most web pages need CSS, JavaScript, and images. FastAPI makes it easy to serve these static files alongside your HTML:
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates
app = FastAPI()
# Mount the static directory
app.mount("/static", StaticFiles(directory="static"), name="static")
templates = Jinja2Templates(directory="templates")
@app.get("/styled-page", response_class=HTMLResponse)
async def read_styled_page(request: Request):
return templates.TemplateResponse("styled.html", {"request": request})
Create a static
directory with a styles.css
file:
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background-color: #f5f5f5;
}
h1 {
color: #333;
}
And a templates/styled.html
file:
<!DOCTYPE html>
<html>
<head>
<title>Styled Page</title>
<link href="{{ url_for('static', path='/styles.css') }}" rel="stylesheet">
</head>
<body>
<h1>Styled HTML Response</h1>
<p>This page includes CSS from a static file</p>
</body>
</html>
The key points here are:
- Use
app.mount()
to make the static directory accessible - Use
url_for()
in templates to generate URLs to static files
Real-world Example: Simple Dashboard
Let's create a more comprehensive example of an HTML response in FastAPI - a simple dashboard that displays data:
from fastapi import FastAPI, Request
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from fastapi.staticfiles import StaticFiles
import datetime
app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
templates = Jinja2Templates(directory="templates")
# Mock database data
items_data = [
{"id": 1, "name": "Laptop", "stock": 100, "category": "Electronics"},
{"id": 2, "name": "Desk Chair", "stock": 45, "category": "Furniture"},
{"id": 3, "name": "Coffee Mug", "stock": 200, "category": "Kitchen"},
{"id": 4, "name": "Monitor", "stock": 75, "category": "Electronics"},
{"id": 5, "name": "Notebook", "stock": 180, "category": "Stationery"},
]
@app.get("/dashboard", response_class=HTMLResponse)
async def dashboard(request: Request):
current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
total_stock = sum(item["stock"] for item in items_data)
return templates.TemplateResponse(
"dashboard.html",
{
"request": request,
"items": items_data,
"total_stock": total_stock,
"current_time": current_time,
"num_categories": len(set(item["category"] for item in items_data))
}
)
Create a templates/dashboard.html
file:
<!DOCTYPE html>
<html>
<head>
<title>Inventory Dashboard</title>
<link href="{{ url_for('static', path='/dashboard.css') }}" rel="stylesheet">
</head>
<body>
<div class="dashboard">
<header>
<h1>Inventory Dashboard</h1>
<p>Current time: {{ current_time }}</p>
</header>
<div class="summary">
<div class="stat-card">
<h3>Total Items</h3>
<p>{{ items|length }}</p>
</div>
<div class="stat-card">
<h3>Total Stock</h3>
<p>{{ total_stock }}</p>
</div>
<div class="stat-card">
<h3>Categories</h3>
<p>{{ num_categories }}</p>
</div>
</div>
<div class="data-table">
<h2>Inventory Items</h2>
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
<th>Stock</th>
<th>Category</th>
</tr>
</thead>
<tbody>
{% for item in items %}
<tr>
<td>{{ item.id }}</td>
<td>{{ item.name }}</td>
<td>{{ item.stock }}</td>
<td>{{ item.category }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</body>
</html>
And create a static/dashboard.css
file for styling:
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
margin: 0;
padding: 0;
background-color: #f5f7fa;
}
.dashboard {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
header {
background-color: #fff;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
header h1 {
margin: 0;
color: #333;
}
.summary {
display: flex;
gap: 20px;
margin-bottom: 20px;
}
.stat-card {
background-color: #fff;
border-radius: 8px;
padding: 20px;
flex: 1;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
text-align: center;
}
.stat-card h3 {
margin-top: 0;
color: #666;
}
.stat-card p {
font-size: 24px;
font-weight: bold;
margin: 0;
color: #333;
}
.data-table {
background-color: #fff;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 12px 15px;
text-align: left;
border-bottom: 1px solid #eee;
}
thead {
background-color: #f8f9fa;
}
th {
color: #666;
}
This example demonstrates a complete dashboard with:
- Dynamic data rendering
- Multiple template variables
- CSS styling
- Jinja2 template logic (loops and filters)
Best Practices for HTML Responses
When working with HTML responses in FastAPI, keep these best practices in mind:
- Separate concerns: Keep your HTML templates separate from your Python code
- Use a template engine: For anything beyond simple HTML, use a template engine like Jinja2
- Consider safety: Be careful with user-provided data to avoid XSS attacks
- Performance: For complex applications, consider using a frontend framework instead
- Caching: Implement appropriate caching for static HTML content
- Content Security Policy: Consider adding appropriate headers for security
Summary
FastAPI makes it straightforward to serve HTML content alongside your API endpoints. We've covered:
- Basic HTML responses using
HTMLResponse
- Dynamic HTML generation with path and query parameters
- Template rendering with Jinja2
- Serving static files with your HTML content
- A complete real-world example of a dashboard
While FastAPI is primarily designed for building APIs, its HTML response capabilities make it suitable for simple web applications or hybrid systems that combine API and web interfaces.
Additional Resources and Exercises
Further Reading
Exercises
-
Basic HTML Form: Create a simple HTML form that submits data to a FastAPI endpoint and returns an HTML response with the submitted data.
-
Template Inheritance: Extend the dashboard example to use Jinja2 template inheritance with a base template.
-
Interactive Dashboard: Add JavaScript to the dashboard example to make it interactive (e.g., sorting tables, filtering data).
-
Markdown to HTML: Create an endpoint that converts Markdown content to HTML using a library like
markdown2
and returns it as an HTML response. -
Secure Content: Research and implement Content Security Policy headers for your HTML responses to improve security.
By mastering HTML responses in FastAPI, you gain the flexibility to build applications that serve both API consumers and direct users through web interfaces.
If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)