from fasthtml.common import *Response Types
FastHTML provides multiple HTTP response types that automatically set the appropriate HTTP content type and handle serialization. The main response types are:
- FT components
- Redirects (HTTP 303 and other 3xx codes)
- JSON (for API endpoints)
- Streams (EventStream, for Server-Side Events)
- Plaintext
Websockets have their own protocol and don’t follow the HTTP request/response cycle. To learn more, check out our explanation about websockets here.
Configuration
app,rt = fast_app()app and rt are the common FastHTML route handler decorators. We instantiate them with the fast_app function.
cli = Client(app)FastHTML comes with the test client named Client. It allows us to test handlers via a simple interface where .get() is a HTTP GET request, .post() is a HTTP POST request.
FT Component Response
@rt('/ft')
def get(): return Html(Div('FT Component Response'))This is the response type you’re probably most familiar with. Here the route handler returns an FT component, which FastHTML wraps in an HTML document with a head and body.
print(cli.get('/ft').text) <!doctype html>
<html>
<div>FT Component Response</div>
</html>
Redirect Response
@rt('/rr')
def get(): return Redirect('https://fastht.ml/')Here in this route handler, Redirect redirects the user’s browser to the new URL ‘https://fastht.ml/’
resp = cli.get('/rr')
print(resp.url)
print(resp.status_code)http://testserver/rr
303
You can see the URL in the response headers and url attribute, as well as a status code of 303.
JSON Response
@rt('/json')
def get(): return {'hello': 'world'}This route handler returns a JSON response, where the content-type has been set to .
resp = cli.get('/json')
print(resp.headers)
print(resp.json())Headers({'content-length': '17', 'content-type': 'application/json'})
{'hello': 'world'}
You can see that the Content-Type header has been set to application/json, and that the response is simply the JSON without any HTML wrapping it.
EventStream
from time import sleep
def counter():
"""Counter is an generator that
publishes a number every second.
"""
for i in range(3):
yield sse_message(f"Event {i}")
sleep(1)
@rt('/stream')
def get():
return EventStream(counter())With server-sent events, it’s possible for a server to send new data to a web page at any time, by pushing messages to the web page. Unlike WebSockets, SSE can only go in one direction: server to client. SSE is also part of the HTTP specification unlike WebSockets which uses its own specification.
resp = cli.get('/stream')
print(resp.text)event: message
data: Event 0
event: message
data: Event 1
event: message
data: Event 2
Each one of the message events above arrived one second after the previous message event.
Plaintext Response
@rt('/text')
def get(): return 'Hello world'When you return a string from a route handler, you get a plain-text response.
print(cli.get('/text').text)Hello world
Here you can see that the response text is simply the string you returned, without any HTML wrapping it.