maces / fastapi-htmx

Extension for FastAPI to make HTMX easier to use.
GNU Lesser General Public License v3.0
206 stars 8 forks source link

Sending (HX-Trigger) headers in response #46

Open hf-kklein opened 3 months ago

hf-kklein commented 3 months ago

In plain FastAPI I can add headers to my response as described here

def get_headers(response: Response):
    response.headers["X-Cat-Dog"] = "alone in the world"
    return {"foo": "bar"} # this is fed to the jinja2 template

But this doesn't work when using an @htmx decorated endpoint. The response and its headers are ignored and overwritten with the Template response.

Seems like a bug or at least missing feature to me.

hf-kklein commented 3 months ago

For anyone having the same issue:

from functools import wraps
from typing import Awaitable, Callable

from fastapi import Response

_DecoratedFuncType = Callable[..., Awaitable[Response]]

def add_hx_trigger_header_on_success(header_value: str) -> Callable[[_DecoratedFuncType], _DecoratedFuncType]:
    """
    Adds the 'HX-Trigger' header to the response if the response status code is a success code.
    See https://htmx.org/attributes/hx-trigger/#triggering-via-the-hx-trigger-header and
    https://htmx.org/headers/hx-trigger/ for details on this header.
    This is a workaround for https://github.com/maces/fastapi-htmx/issues/46
    :param header_value: the value to set for the 'HX-Trigger' header
    """

    def decorator(func: _DecoratedFuncType) -> _DecoratedFuncType:
        @wraps(func)
        async def wrapper(*args, **kwargs):
            response = await func(*args, **kwargs)
            if isinstance(response, Response):
                is_success_status_code = 200 <= response.status_code < 300
                if is_success_status_code:
                    response.headers.append("HX-Trigger", header_value)

            return response

        return wrapper

    return decorator

and then use it like:

@router.post("/foo", response_class=HTMLResponse, status_code=status.HTTP_201_CREATED)
@add_hx_trigger_header_on_success('my-magic-event')
@htmx("my-template-path", "index")
async def create_foo_htmx(
    request: Request
):
    ...

and

    <anHtmlTag hx-get="..." hx-trigger="load, my-magic-event from:body">
maces commented 3 months ago

Thanks for the workaround. Linking #31 for reference. Will keep this open until it is supported officially.