Skip to content

Instantly share code, notes, and snippets.

@axelknock
Created November 22, 2024 18:06
Show Gist options
  • Select an option

  • Save axelknock/2a52bc818a70600652fe34b551fd629e to your computer and use it in GitHub Desktop.

Select an option

Save axelknock/2a52bc818a70600652fe34b551fd629e to your computer and use it in GitHub Desktop.
Basic example of middleware to alter dataclass response from FT to json
from fasthtml.common import *
# Create middleware to handle requests ending in .json
class JSONRedirectMiddleware(MiddlewareBase):
def __init__(self, app):
self.app = app
# pass along the request with the application/json header
# if the route excepts it (as defined below, the response will be JSON)
async def __call__(self, scope, receive, send):
path = scope["path"]
if path.endswith(".json"):
# Remove .json suffix and add json header
scope["path"] = path.rsplit(".json", 1)[0]
scope["headers"] = [
(b"accept", b"application/json"),
*[h for h in scope["headers"] if h[0] != b"accept"],
]
return await self.app(scope, receive, send)
middleware = [
Middleware(JSONRedirectMiddleware),
]
app, rt = fast_app(
live=True,
middleware=middleware,
)
# Define model with FT method, which FastHTML will handle by default
@dataclass
class Todo:
id: int
task: str
done: bool
def __ft__(self):
return Li(id=f"todo-{self.id}")(
"✓" if self.done else "❌",
" ",
self.task,
Span(
" ",
A("FT", href=f"/todo/{self.id}"),
" ",
A("JSON", href=f"/todo/{self.id}.json"),
),
)
# Define which classes should also have JSON responses when requested
def add_response_chooser_to_classes(*classes):
def response_method(self, req: Optional[Request] = None):
if req and req.headers.get("accept") == "application/json":
return JSONResponse(asdict(self))
return self.__ft__()
for cls in classes:
cls.__response__ = response_method
add_response_chooser_to_classes(Todo)
# Create and seed database
def make_db():
db = Database("todos.db")
todos = db.create(Todo, name="todos")
# sample data
if not todos.count > 0:
todos.insert(Todo(task="Make gist", done=True), ignore=True)
todos.insert(Todo(task="Share gist", done=False), ignore=True)
return db, todos
db, todos = make_db()
@rt("/todo/{id}")
async def todo(id: int):
return todos[id]
@rt("/")
def index():
return Titled(
"Todos with Middleware",
Ul(
*[
Div(hx_get=f"/todo/{todo.id}", hx_trigger="load", hx_swap="outerHTML")
for todo in todos()
]
),
)
serve()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment