0
# Advanced Response Types
1
2
FastAPI provides a comprehensive set of response classes for different content types and use cases, including high-performance JSON responses, HTML responses, file serving, streaming, and redirects. These response types enable efficient content delivery optimized for specific scenarios and client requirements.
3
4
## Capabilities
5
6
### High-Performance JSON Responses
7
8
Ultra-fast JSON response classes using optimized JSON libraries for maximum performance.
9
10
```python { .api }
11
class UJSONResponse(Response):
12
def __init__(
13
self,
14
content: Any = None,
15
status_code: int = 200,
16
headers: dict = None,
17
media_type: str = "application/json",
18
background: BackgroundTask = None,
19
) -> None:
20
"""
21
JSON response using ujson for high performance.
22
23
Parameters:
24
- content: Data to serialize as JSON
25
- status_code: HTTP status code
26
- headers: Additional HTTP headers
27
- media_type: Content type header
28
- background: Background task to run after response
29
"""
30
31
class ORJSONResponse(Response):
32
def __init__(
33
self,
34
content: Any = None,
35
status_code: int = 200,
36
headers: dict = None,
37
media_type: str = "application/json",
38
background: BackgroundTask = None,
39
) -> None:
40
"""
41
JSON response using orjson for maximum performance.
42
43
Parameters:
44
- content: Data to serialize as JSON
45
- status_code: HTTP status code
46
- headers: Additional HTTP headers
47
- media_type: Content type header
48
- background: Background task to run after response
49
"""
50
```
51
52
### HTML and Plain Text Responses
53
54
Response classes for serving HTML content and plain text.
55
56
```python { .api }
57
class HTMLResponse(Response):
58
def __init__(
59
self,
60
content: str = "",
61
status_code: int = 200,
62
headers: dict = None,
63
media_type: str = "text/html",
64
background: BackgroundTask = None,
65
) -> None:
66
"""
67
HTML response for serving HTML content.
68
69
Parameters:
70
- content: HTML content string
71
- status_code: HTTP status code
72
- headers: Additional HTTP headers
73
- media_type: Content type header
74
- background: Background task to run after response
75
"""
76
77
class PlainTextResponse(Response):
78
def __init__(
79
self,
80
content: str = "",
81
status_code: int = 200,
82
headers: dict = None,
83
media_type: str = "text/plain",
84
background: BackgroundTask = None,
85
) -> None:
86
"""
87
Plain text response.
88
89
Parameters:
90
- content: Plain text content string
91
- status_code: HTTP status code
92
- headers: Additional HTTP headers
93
- media_type: Content type header
94
- background: Background task to run after response
95
"""
96
```
97
98
### Redirect Responses
99
100
Response class for HTTP redirects with configurable status codes.
101
102
```python { .api }
103
class RedirectResponse(Response):
104
def __init__(
105
self,
106
url: str,
107
status_code: int = 307,
108
headers: dict = None,
109
background: BackgroundTask = None,
110
) -> None:
111
"""
112
HTTP redirect response.
113
114
Parameters:
115
- url: Target URL for redirect
116
- status_code: HTTP redirect status code (301, 302, 307, 308)
117
- headers: Additional HTTP headers
118
- background: Background task to run after response
119
"""
120
```
121
122
### File and Streaming Responses
123
124
Response classes for serving files and streaming large content.
125
126
```python { .api }
127
class FileResponse(Response):
128
def __init__(
129
self,
130
path: str,
131
status_code: int = 200,
132
headers: dict = None,
133
media_type: str = None,
134
filename: str = None,
135
background: BackgroundTask = None,
136
) -> None:
137
"""
138
File download response.
139
140
Parameters:
141
- path: File system path to the file
142
- status_code: HTTP status code
143
- headers: Additional HTTP headers
144
- media_type: Content type (auto-detected if None)
145
- filename: Download filename (Content-Disposition header)
146
- background: Background task to run after response
147
"""
148
149
class StreamingResponse(Response):
150
def __init__(
151
self,
152
content: Iterator[Any],
153
status_code: int = 200,
154
headers: dict = None,
155
media_type: str = None,
156
background: BackgroundTask = None,
157
) -> None:
158
"""
159
Streaming response for large content.
160
161
Parameters:
162
- content: Iterator yielding content chunks
163
- status_code: HTTP status code
164
- headers: Additional HTTP headers
165
- media_type: Content type header
166
- background: Background task to run after response
167
"""
168
```
169
170
## Usage Examples
171
172
### High-Performance JSON Responses
173
174
```python
175
from fastapi import FastAPI
176
from fastapi.responses import UJSONResponse, ORJSONResponse
177
import time
178
179
app = FastAPI()
180
181
# Large dataset for performance comparison
182
large_data = {
183
"users": [
184
{"id": i, "name": f"User {i}", "score": i * 1.5}
185
for i in range(10000)
186
],
187
"timestamp": time.time(),
188
"metadata": {"total": 10000, "version": "1.0"}
189
}
190
191
@app.get("/data/ujson", response_class=UJSONResponse)
192
def get_data_ujson():
193
"""Return large dataset using ujson for faster serialization."""
194
return large_data
195
196
@app.get("/data/orjson", response_class=ORJSONResponse)
197
def get_data_orjson():
198
"""Return large dataset using orjson for maximum performance."""
199
return large_data
200
201
# Set as default response class for entire app
202
app_with_orjson = FastAPI(default_response_class=ORJSONResponse)
203
204
@app_with_orjson.get("/fast")
205
def fast_endpoint():
206
return {"message": "This uses ORJSONResponse by default"}
207
```
208
209
### HTML Responses
210
211
```python
212
from fastapi import FastAPI, Request
213
from fastapi.responses import HTMLResponse
214
215
app = FastAPI()
216
217
@app.get("/", response_class=HTMLResponse)
218
def home():
219
html_content = """
220
<!DOCTYPE html>
221
<html>
222
<head>
223
<title>FastAPI HTML Response</title>
224
<style>
225
body { font-family: Arial, sans-serif; margin: 40px; }
226
.header { color: #2c3e50; }
227
</style>
228
</head>
229
<body>
230
<h1 class="header">Welcome to FastAPI</h1>
231
<p>This is a direct HTML response.</p>
232
<ul>
233
<li><a href="/api/users">API Users</a></li>
234
<li><a href="/api/docs">API Documentation</a></li>
235
</ul>
236
</body>
237
</html>
238
"""
239
return HTMLResponse(content=html_content, status_code=200)
240
241
@app.get("/dynamic/{name}", response_class=HTMLResponse)
242
def dynamic_page(name: str):
243
html_content = f"""
244
<!DOCTYPE html>
245
<html>
246
<head>
247
<title>Hello {name}</title>
248
</head>
249
<body>
250
<h1>Hello, {name}!</h1>
251
<p>This page was generated dynamically.</p>
252
<a href="/">Back to Home</a>
253
</body>
254
</html>
255
"""
256
return HTMLResponse(content=html_content)
257
258
@app.get("/error-page", response_class=HTMLResponse)
259
def error_page():
260
html_content = """
261
<html>
262
<body>
263
<h1>Something went wrong</h1>
264
<p>Please try again later.</p>
265
</body>
266
</html>
267
"""
268
return HTMLResponse(content=html_content, status_code=500)
269
```
270
271
### Redirect Responses
272
273
```python
274
from fastapi import FastAPI, Form
275
from fastapi.responses import RedirectResponse
276
277
app = FastAPI()
278
279
@app.get("/old-url")
280
def old_endpoint():
281
# Permanent redirect (301)
282
return RedirectResponse(url="/new-url", status_code=301)
283
284
@app.get("/new-url")
285
def new_endpoint():
286
return {"message": "This is the new endpoint"}
287
288
@app.post("/login")
289
def login(username: str = Form(...), password: str = Form(...)):
290
# Simulate authentication
291
if username == "admin" and password == "secret":
292
# Temporary redirect after successful login (307)
293
return RedirectResponse(url="/dashboard", status_code=307)
294
else:
295
# Redirect back to login with error
296
return RedirectResponse(url="/login?error=invalid", status_code=303)
297
298
@app.get("/dashboard")
299
def dashboard():
300
return {"message": "Welcome to the dashboard"}
301
302
@app.get("/external-redirect")
303
def external_redirect():
304
# Redirect to external URL
305
return RedirectResponse(url="https://fastapi.tiangolo.com/")
306
307
# Conditional redirects
308
@app.get("/redirect/{target}")
309
def conditional_redirect(target: str):
310
redirect_map = {
311
"docs": "/docs",
312
"redoc": "/redoc",
313
"github": "https://github.com/tiangolo/fastapi",
314
"home": "/"
315
}
316
317
if target in redirect_map:
318
return RedirectResponse(url=redirect_map[target])
319
else:
320
return RedirectResponse(url="/404")
321
```
322
323
### File Responses
324
325
```python
326
from fastapi import FastAPI, HTTPException
327
from fastapi.responses import FileResponse
328
import os
329
from pathlib import Path
330
331
app = FastAPI()
332
333
@app.get("/download/{filename}")
334
def download_file(filename: str):
335
file_path = Path("files") / filename
336
337
if not file_path.exists():
338
raise HTTPException(status_code=404, detail="File not found")
339
340
return FileResponse(
341
path=str(file_path),
342
filename=filename,
343
media_type='application/octet-stream'
344
)
345
346
@app.get("/image/{filename}")
347
def serve_image(filename: str):
348
file_path = Path("images") / filename
349
350
if not file_path.exists():
351
raise HTTPException(status_code=404, detail="Image not found")
352
353
# Auto-detect media type based on file extension
354
return FileResponse(path=str(file_path))
355
356
@app.get("/report/pdf")
357
def download_report():
358
return FileResponse(
359
path="reports/monthly_report.pdf",
360
filename="monthly_report.pdf",
361
media_type="application/pdf"
362
)
363
364
@app.get("/export/csv")
365
def export_data():
366
# Assume CSV file is generated elsewhere
367
return FileResponse(
368
path="exports/data.csv",
369
filename="exported_data.csv",
370
media_type="text/csv",
371
headers={"Custom-Header": "Export-Data"}
372
)
373
```
374
375
### Streaming Responses
376
377
```python
378
from fastapi import FastAPI
379
from fastapi.responses import StreamingResponse
380
import json
381
import time
382
from typing import Iterator
383
384
app = FastAPI()
385
386
def generate_large_csv() -> Iterator[str]:
387
"""Generate large CSV data as iterator."""
388
yield "id,name,email,created_at\n"
389
for i in range(100000):
390
yield f"{i},User{i},user{i}@example.com,2024-01-{(i % 30) + 1:02d}\n"
391
392
@app.get("/export/large-csv")
393
def export_large_csv():
394
return StreamingResponse(
395
generate_large_csv(),
396
media_type="text/csv",
397
headers={"Content-Disposition": "attachment; filename=large_data.csv"}
398
)
399
400
def generate_json_stream() -> Iterator[bytes]:
401
"""Generate streaming JSON response."""
402
yield b'{"data": ['
403
for i in range(1000):
404
if i > 0:
405
yield b','
406
data = {"id": i, "value": f"item_{i}"}
407
yield json.dumps(data).encode("utf-8")
408
yield b']}'
409
410
@app.get("/stream/json")
411
def stream_json():
412
return StreamingResponse(
413
generate_json_stream(),
414
media_type="application/json"
415
)
416
417
def generate_server_sent_events() -> Iterator[str]:
418
"""Generate Server-Sent Events stream."""
419
for i in range(10):
420
yield f"data: Event {i} at {time.time()}\n\n"
421
time.sleep(1)
422
423
@app.get("/events")
424
def server_sent_events():
425
return StreamingResponse(
426
generate_server_sent_events(),
427
media_type="text/plain",
428
headers={"Cache-Control": "no-cache", "Connection": "keep-alive"}
429
)
430
431
async def generate_async_stream() -> Iterator[bytes]:
432
"""Generate async streaming response."""
433
for chunk in range(100):
434
# Simulate async processing
435
await asyncio.sleep(0.01)
436
yield f"Chunk {chunk}\n".encode("utf-8")
437
438
@app.get("/stream/async")
439
async def async_stream():
440
return StreamingResponse(
441
generate_async_stream(),
442
media_type="text/plain"
443
)
444
```
445
446
### Response Headers and Background Tasks
447
448
```python
449
from fastapi import FastAPI, BackgroundTasks
450
from fastapi.responses import JSONResponse, FileResponse
451
import logging
452
453
app = FastAPI()
454
455
def log_download(filename: str, user_ip: str):
456
"""Background task to log file downloads."""
457
logging.info(f"File {filename} downloaded by {user_ip}")
458
459
@app.get("/secure-download/{filename}")
460
def secure_download(filename: str, background_tasks: BackgroundTasks, request: Request):
461
file_path = f"secure_files/{filename}"
462
463
# Add logging as background task
464
background_tasks.add_task(log_download, filename, request.client.host)
465
466
return FileResponse(
467
path=file_path,
468
filename=filename,
469
headers={
470
"X-Custom-Header": "Secure-Download",
471
"Cache-Control": "no-cache"
472
}
473
)
474
475
@app.get("/api/data-with-headers")
476
def data_with_custom_headers():
477
return JSONResponse(
478
content={"data": "response"},
479
headers={
480
"X-API-Version": "1.0",
481
"X-Response-Time": str(time.time()),
482
"Access-Control-Allow-Origin": "*"
483
}
484
)
485
```
486
487
## Types
488
489
```python { .api }
490
from typing import Any, Dict, Iterator, Optional, Union
491
from starlette.responses import Response
492
from starlette.background import BackgroundTask
493
494
# Response content types
495
ResponseContent = Union[str, bytes, Iterator[Any]]
496
497
# Headers type
498
ResponseHeaders = Optional[Dict[str, str]]
499
500
# Background task type
501
BackgroundTaskType = Optional[BackgroundTask]
502
503
# HTTP status codes for redirects
504
RedirectStatusCode = Union[301, 302, 303, 307, 308]
505
```