Skip to main content

FastAPI data APIs with HTMX support.

Project description

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, but simple examples that showcase the basic use of FastHX, please see the examples folder.

If you're looking for a more complex (Jinja2) example with features like active search, lazy-loading, server-sent events, custom server-side HTMX triggers, dialogs, and TailwindCSS and DaisyUI integration, check out this FastAPI-HTMX-Tailwind example.

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

Feel free to ask questions or request new features.

And of course all contributions are welcome, including more documentation, examples, code, and tests.

The goal is to make fasthx a well-rounded project that makes even your most complex HTMX use-cases easy to implement.

Contributors

License - MIT

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

Thank you

Thank you to Smart-Now for supporting the project.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fasthx-2.0.2.tar.gz (14.1 kB view details)

Uploaded Source

Built Distribution

fasthx-2.0.2-py3-none-any.whl (13.8 kB view details)

Uploaded Python 3

File details

Details for the file fasthx-2.0.2.tar.gz.

File metadata

  • Download URL: fasthx-2.0.2.tar.gz
  • Upload date:
  • Size: 14.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/5.1.1 CPython/3.12.7

File hashes

Hashes for fasthx-2.0.2.tar.gz
Algorithm Hash digest
SHA256 76fb80dc61b24555111d212a468f04cde448342b7654e51ac0eed84231272eb0
MD5 06dd2cfdbcf6017c9460298694acfb2a
BLAKE2b-256 c4bd4455b087bd0ee8d0235fef626667ee4f92e75cabf810552f3946dfc30256

See more details on using hashes here.

Provenance

The following attestation bundles were made for fasthx-2.0.2.tar.gz:

Publisher: publish.yml on volfpeter/fasthx

Attestations:

File details

Details for the file fasthx-2.0.2-py3-none-any.whl.

File metadata

  • Download URL: fasthx-2.0.2-py3-none-any.whl
  • Upload date:
  • Size: 13.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/5.1.1 CPython/3.12.7

File hashes

Hashes for fasthx-2.0.2-py3-none-any.whl
Algorithm Hash digest
SHA256 a542cee2ba2e58ce6b7f567a9808eac25cd385c1ab0eca6cda19aef160e09e90
MD5 d736b97c542622664be2827d3157cefd
BLAKE2b-256 54ab37e3d451f90ffc30cf51b1ac3d754def8eaf1695aaab41125db6e6c2dede

See more details on using hashes here.

Provenance

The following attestation bundles were made for fasthx-2.0.2-py3-none-any.whl:

Publisher: publish.yml on volfpeter/fasthx

Attestations:

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page