Package: fasthx
Source code:https://github.com/volfpeter/fasthxDocumentation and examples:https://volfpeter.github.io/fasthxFastHXFastAPI and HTMX, the right way.Key features:Decorator syntaxthat works with FastAPI as one would expect, no need for unused or magic dependencies in routes.Works withany templating engineor server-side rendering library, e.g.markyp-htmlordominate.Built-inJinja2 templating support(even with multiple template folders).Gives the rendering engineaccess to all dependenciesof the decorated route.FastAPIroutes will keep working normally by defaultif they receivenon-HTMXrequests, so the same route can serve data and render HTML at the same time.Correct typingmakes it possible to apply other (typed) decorators to your routes.Works with bothsyncandasync routes.InstallationThe package is available on PyPI and can be installed with:$pipinstallfasthxExamplesFor complete examples, please see theexamplesfolder.Jinja2 templatingTo start serving HTML and HTMX requests, all you need to do is create an instance offasthx.Jinjaand use itshx()orpage()methods as decorators on your routes.hx()only triggers HTML rendering for HTMX requests, whilepage()unconditionally renders HTML, saving you some boilerplate code. See the example code below:fromfastapiimportFastAPIfromfastapi.templatingimportJinja2TemplatesfromfasthximportJinjafrompydanticimportBaseModel# Pydantic model of the data the example API is using.classUser(BaseModel):first_name:strlast_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")defindex()->None:...@app.get("/user-list")@jinja.hx("user-list.html")asyncdefhtmx_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)defhtmx_only()->list[User]:return[User(first_name="Billy",last_name="Shears")]Custom templatingIf you're not into Jinja templating, thehx()andpage()decorators give you all the flexibility you need: you can integrate any HTML rendering or templating engine intofasthxsimply by implementing theHTMLRendererprotocol. Similarly to the Jinja case,hx()only triggers HTML rendering for HTMX requests, whilepage()unconditionally renders HTML. See the example code below:fromtypingimportAnnotated,AnyfromfastapiimportDepends,FastAPI,Requestfromfasthximporthx,page# Create the app.app=FastAPI()# Create a dependecy to see that its return value is available in the render function.defget_random_number()->int:return4# 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.defrender_index(result:list[dict[str,str]],*,context:dict[str,Any],request:Request)->str:return"<h1>Hello FastHX</h1>"defrender_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>"foruinresult),"</ul>"))returnf"{lucky_number}\n{users}"@app.get("/")@page(render_index)defindex()->None:...@app.get("/htmx-or-data")@hx(render_user_list)defhtmx_or_data(random_number:DependsRandomNumber)->list[dict[str,str]]:return[{"name":"Joe"}]@app.get("/htmx-only")@hx(render_user_list,no_data=True)asyncdefhtmx_only(random_number:DependsRandomNumber)->list[dict[str,str]]:return[{"name":"Joe"}]DependenciesThe only dependency of this package isfastapi.DevelopmentUserufffor linting and formatting,mypyfor static code analysis, andpytestfor testing.The documentation is built withmkdocs-materialandmkdocstrings.ContributingAll contributions are welcome.ContributorsPeter VolfHasan Sezer TaşanLicense - MITThe package is open-sourced under the conditions of theMIT license.