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
= fast_app() app,rt
app
and rt
are the common FastHTML route handler decorators. We instantiate them with the fast_app
function.
= Client(app) cli
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/’
= cli.get('/rr')
resp 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 .
= cli.get('/json')
resp 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}")
1)
sleep(
@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.
= cli.get('/stream')
resp 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.