Security News
Research
Data Theft Repackaged: A Case Study in Malicious Wrapper Packages on npm
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Source code: https://github.com/volfpeter/fasthx
Documentation and examples: https://volfpeter.github.io/fasthx
FastAPI server-side rendering with built-in HTMX support.
Key features:
htmy
, jinja2
, or dominate
.The package is available on PyPI and can be installed with:
$ pip install fasthx
The package has optional dependencies for the following official integrations:
For complete, but simple examples that showcase the basic use of FastHX
, please see the examples folder.
Requires: pip install fasthx[htmy]
.
Serving HTML and HTMX requests with htmy is as easy as creating a fasthx.htmy.HTMY
instance and using its hx()
and page()
decorator methods on your routes.
The example below assumes the existence of an IndexPage
and a UserList
htmy
component. The full working example with the htmy
components can be found here.
from datetime import date
from fastapi import FastAPI
from pydantic import BaseModel
from fasthx.htmy import HTMY
# Pydantic model for the application
class User(BaseModel):
name: str
birthday: date
# Create the FastAPI application.
app = FastAPI()
# Create the FastHX HTMY instance that renders all route results.
htmy = HTMY()
@app.get("/users")
@htmy.hx(UserList) # Render the result using the UserList component.
def get_users(rerenders: int = 0) -> list[User]:
return [
User(name="John", birthday=date(1940, 10, 9)),
User(name="Paul", birthday=date(1942, 6, 18)),
User(name="George", birthday=date(1943, 2, 25)),
User(name="Ringo", birthday=date(1940, 7, 7)),
]
@app.get("/")
@htmy.page(IndexPage) # Render the index page.
def index() -> None: ...
Requires: pip install fasthx[jinja]
.
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. 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")]
See the full working example here.
Requires: pip install fasthx
.
If you would like to use a rendering engine without FastHX integration, you can easily build on the hx()
and page()
decorators which give you all the functionality you will need. All you need to do is implement 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"}]
See the full working example here.
Jinja2
example with features like active search, lazy-loading, server-sent events, custom server-side HTMX triggers, dialogs, and TailwindCSS and DaisyUI integration.The only dependency of this package is fastapi
.
Use ruff
for linting and formatting, mypy
for static code analysis, and pytest
for testing.
The documentation is built with mkdocs-material
and mkdocstrings
.
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.
The package is open-sourced under the conditions of the MIT license.
Thank you to Smart-Now for supporting the project.
FAQs
FastAPI server-side rendering with built-in HTMX support.
We found that fasthx demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Research
The Socket Research Team breaks down a malicious wrapper package that uses obfuscation to harvest credentials and exfiltrate sensitive data.
Research
Security News
Attackers used a malicious npm package typosquatting a popular ESLint plugin to steal sensitive data, execute commands, and exploit developer systems.
Security News
The Ultralytics' PyPI Package was compromised four times in one weekend through GitHub Actions cache poisoning and failure to rotate previously compromised API tokens.