• Stars
    star
    394
  • Rank 108,560 (Top 3 %)
  • Language
    Python
  • License
    MIT License
  • Created 8 months ago
  • Updated 13 days ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

FastAPI and HTMX, the right way.

Tests Linters Documentation PyPI package

Source code: https://github.com/volfpeter/fasthx

Documentation and examples: https://volfpeter.github.io/fasthx

FastHX

FastAPI and HTMX, the right way.

Key features:

  • Decorator syntax that works with FastAPI as one would expect, no need for unused or magic dependencies in routes.
  • Works with any templating engine or server-side rendering library, e.g. markyp-html or dominate.
  • Built-in Jinja2 templating support (even with multiple template folders).
  • Gives the rendering engine access to all dependencies of the decorated route.
  • FastAPI routes will keep working normally by default if they receive non-HTMX requests, so the same route can serve data and render HTML at the same time.
  • Response headers you set in your routes are kept after rendering, as you would expect in FastAPI.
  • Correct typing makes it possible to apply other (typed) decorators to your routes.
  • Works with both sync and async routes.

Installation

The package is available on PyPI and can be installed with:

$ pip install fasthx

Examples

For complete examples, please see the examples folder.

Jinja2 templating

To start serving HTML and HTMX requests, all you need to do is create an instance of fasthx.Jinja and use its hx() or page() methods as decorators on your routes. hx() only triggers HTML rendering for HTMX requests, while page() unconditionally renders HTML, saving you some boilerplate code. See the example code below:

from fastapi import FastAPI
from fastapi.templating import Jinja2Templates
from fasthx import Jinja
from pydantic import BaseModel

# Pydantic model of the data the example API is using.
class User(BaseModel):
    first_name: str
    last_name: str

# Create the app.
app = FastAPI()

# Create a FastAPI Jinja2Templates instance and use it to create a
# FastHX Jinja instance that will serve as your decorator.
jinja = Jinja(Jinja2Templates("templates"))

@app.get("/")
@jinja.page("index.html")
def index() -> None:
    ...

@app.get("/user-list")
@jinja.hx("user-list.html")
async def htmx_or_data() -> list[User]:
    return [
        User(first_name="John", last_name="Lennon"),
        User(first_name="Paul", last_name="McCartney"),
        User(first_name="George", last_name="Harrison"),
        User(first_name="Ringo", last_name="Starr"),
    ]

@app.get("/admin-list")
@jinja.hx("user-list.html", no_data=True)
def htmx_only() -> list[User]:
    return [User(first_name="Billy", last_name="Shears")]

Custom templating

If you're not into Jinja templating, the hx() and page() decorators give you all the flexibility you need: you can integrate any HTML rendering or templating engine into fasthx simply by implementing the HTMLRenderer protocol. Similarly to the Jinja case, hx() only triggers HTML rendering for HTMX requests, while page() unconditionally renders HTML. See the example code below:

from typing import Annotated, Any

from fastapi import Depends, FastAPI, Request
from fasthx import hx, page

# Create the app.
app = FastAPI()

# Create a dependecy to see that its return value is available in the render function.
def get_random_number() -> int:
    return 4  # Chosen by fair dice roll.

DependsRandomNumber = Annotated[int, Depends(get_random_number)]

# Create the render methods: they must always have these three arguments.
# If you're using static type checkers, the type hint of `result` must match
# the return type annotation of the route on which this render method is used.
def render_index(result: list[dict[str, str]], *, context: dict[str, Any], request: Request) -> str:
    return "<h1>Hello FastHX</h1>"

def render_user_list(result: list[dict[str, str]], *, context: dict[str, Any], request: Request) -> str:
    # The value of the `DependsRandomNumber` dependency is accessible with the same name as in the route.
    random_number = context["random_number"]
    lucky_number = f"<h1>{random_number}</h1>"
    users = "".join(("<ul>", *(f"<li>{u['name']}</li>" for u in result), "</ul>"))
    return f"{lucky_number}\n{users}"

@app.get("/")
@page(render_index)
def index() -> None:
    ...

@app.get("/htmx-or-data")
@hx(render_user_list)
def htmx_or_data(random_number: DependsRandomNumber) -> list[dict[str, str]]:
    return [{"name": "Joe"}]

@app.get("/htmx-only")
@hx(render_user_list, no_data=True)
async def htmx_only(random_number: DependsRandomNumber) -> list[dict[str, str]]:
    return [{"name": "Joe"}]

Dependencies

The only dependency of this package is fastapi.

Development

Use ruff for linting and formatting, mypy for static code analysis, and pytest for testing.

The documentation is built with mkdocs-material and mkdocstrings.

Contributing

All contributions are welcome.

Contributors

License - MIT

The package is open-sourced under the conditions of the MIT license.

More Repositories

1

markyp-bootstrap4

Create Bootstrap 4 web pages using purely Python.
Python
20
star
2

localclustering

Python 3 implementation and documentation of the Hermina-Janos local graph clustering algorithm.
Python
19
star
3

fastapi-htmx-tailwind-example

Example application (IoT dashboard) built with FastAPI, HTMX, TailwindCSS, DaisyUI, Jinja, and MongoDB.
Python
19
star
4

markyp

Python 3 tools for creating markup documents.
Python
13
star
5

motorhead

Async MongoDB with vanilla Pydantic v2+ - made easy.
Python
11
star
6

mastodon-social-graph

Mastodon social graph with an SQL backend, in-memory cache, and built-in, on-demand web scraper.
Python
10
star
7

uspto-patent-citation-graph

Graph that downloads patent citation data from USPTO's PatentsView API on-demand and stores it locally in an SQL database (and in memory) for fast access later.
Python
9
star
8

markyp-html

HTML element implementations based on markyp
Python
7
star
9

graphscraper

Python 3 graph implementation designed to be turned into a web scraper for graph data.
Python
6
star
10

codenames-kivy

Table and word-list generator for the popular Codenames boardgame, created in Python 3 using Kivy.
Python
3
star
11

fastapi-motor-oil

Collection of async utilities for working with MongoDB and conveniently creating performant APIs with async web frameworks such a FastAPI
Python
3
star
12

timer

Minimalist timer application developed for elementary OS
Vala
2
star
13

fastapi-flask-auth

Lightweight FastAPI dependencies and authenticator that uses Flask session cookies for access control
Python
2
star
14

sqlmodelservice

A generic service layer on top of SQLModel for conveniently creating APIs with frameworks like FastAPI
Python
2
star
15

markyp-highlightjs

HTML code highlighting markyp extension based on highlight.js
Python
2
star
16

flask-session-decoder

Zero-dependency Flask session decoder
Python
2
star
17

markyp-rss

markyp-based RSS 2.0 implementation.
Python
1
star
18

pyt

Python dev tools and utilities.
Python
1
star
19

mongo-user-blueprint

Demo `Flask` application showing how to use the `user-blueprint` project to set up a MongoDB-based user handling web application.
Python
1
star
20

coupon-app

Example application using FastAPI + sqlmodel
Python
1
star
21

user-blueprint

Lightweight Flask user handler blueprint.
Python
1
star