Skip to main content

approck-fastapi-utils

Small utilities for FastAPI services: JWT payload decoding from headers, standardized JSON responses, reusable exception handlers, a Pydantic-friendly comma-separated list type, and optional SQLAlchemy error handlers.

Python: 3.10+

Install

From PyPI:

uv add approck-fastapi-utils

Or with pip:

pip install approck-fastapi-utils

Optional SQLAlchemy handlers:

uv add "approck-fastapi-utils[sqlalchemy]"

Optional Redis store for production idempotency:

uv add "approck-fastapi-utils[redis]"

What is included

Module Purpose
approck_fastapi_utils Public re-exports of the most common symbols
approck_fastapi_utils.auth ensure_current_user, optional_current_user, ensure_current_superuser, JwtPayload
approck_fastapi_utils.jwt encode_payload, decode_payload, get_token_from_header
approck_fastapi_utils.exceptions CustomException hierarchy with HTTP status codes
approck_fastapi_utils.exception_handlers Handlers and register_exception_handlers(profile=...)
approck_fastapi_utils.responses JSON responses and OpenAPI schema helpers
approck_fastapi_utils.response Prebuilt 404 response schema
approck_fastapi_utils.types CommaSeparatedList for query parameters
approck_fastapi_utils.testing JWT/auth helpers for pytest
approck_fastapi_utils.gateway build_gateway_headers for proxy routes
approck_fastapi_utils.idempotency IdempotencyMiddleware, InMemoryIdempotencyStore
approck_fastapi_utils.idempotency.stores.redis RedisIdempotencyStore (requires the redis extra)
approck_fastapi_utils.sqlalchemy.exception_handlers Handlers for DBAPIError and NoResultFound (requires the sqlalchemy extra)

Usage

Register exception handlers

Use a preset instead of wiring handlers manually:

from fastapi import FastAPI

from approck_fastapi_utils import register_exception_handlers

app = FastAPI()
register_exception_handlers(app, profile="api")

Profiles:

Profile Handlers
minimal HTTPException, CustomException
api minimal + RequestValidationError, NoResultFound
internal api + DBAPIError (with optional database_sanitize=True)

CustomException and HTTP status codes

Built-in exceptions map to HTTP codes automatically. Subclasses can set status_code on the class or pass it to __init__:

from approck_fastapi_utils import Conflict, CustomException, NotFound

class OrderAlreadyPaid(CustomException):
    status_code = 409

raise Conflict("already exists")          # -> 409
raise NotFound("missing")                   # -> 404
raise CustomException("bad", status_code=418)

Error body contract: {"successful": false, "code": "...", "detail": "..."}.

JWT payload header

ensure_current_user reads X-JWT-Payload (URL-safe base64 JSON). Invalid payloads raise Unauthorized (401).

from fastapi import APIRouter, Depends

from approck_fastapi_utils import ensure_current_user, optional_current_user
from approck_fastapi_utils.responses import SuccessfulResponse

router = APIRouter()

@router.get("/me")
async def read_me(payload: dict = Depends(ensure_current_user)):
    return SuccessfulResponse()

@router.get("/public")
async def public(payload: dict | None = Depends(optional_current_user())):
    ...

JWT encode/decode

from approck_fastapi_utils import decode_payload, encode_payload

payload = {"user_id": 1, "is_superuser": False}
encoded = encode_payload(payload)
claims = decode_payload(encoded)  # returns None on invalid input

Testing helpers

from approck_fastapi_utils.testing import auth_headers, override_current_user

headers = auth_headers(user_id=42, is_superuser=True)
override_current_user(app, user_id=42)

Standard JSON responses and OpenAPI examples

from approck_fastapi_utils import HTTP_404_NOT_FOUND, SuccessfulResponse, custom_exception_response_schema, error_response_schema
from approck_fastapi_utils.exceptions import NotFound

@router.get(
    "/health",
    responses={
        **SuccessfulResponse.schema(),
        **error_response_schema(400, "Bad request"),
        **custom_exception_response_schema(NotFound),
        **HTTP_404_NOT_FOUND,
    },
)
async def health():
    return SuccessfulResponse()

Comma-separated query lists

CommaSeparatedList[T] accepts ?ids=1,2,3. Whitespace is trimmed, empty string yields [].

from typing import Annotated

from fastapi import Query

from approck_fastapi_utils import CommaSeparatedList

ids: Annotated[CommaSeparatedList[int], Query(description="Example: 1,2,3")]

Gateway/proxy headers

from approck_fastapi_utils import build_gateway_headers

headers = build_gateway_headers(
    authorization=request.headers.get("Authorization"),
    x_jwt_payload=request.headers.get("X-JWT-Payload"),
)

Idempotency middleware

Use Idempotency-Key on POST/PUT/PATCH/DELETE requests. Successful responses (< 400) are cached and replayed for duplicate keys. Payload mismatches return HTTP 409.

import redis.asyncio as aioredis
from fastapi import FastAPI

from approck_fastapi_utils import IdempotencyMiddleware
from approck_fastapi_utils.idempotency.stores.redis import RedisIdempotencyStore

redis_client = aioredis.from_url("redis://localhost:6379/0", decode_responses=True)
store = RedisIdempotencyStore(redis_client)

app = FastAPI()
app.add_middleware(
    IdempotencyMiddleware,
    store=store,
    ttl=300,
    validate_signature=True,
)

For tests, use InMemoryIdempotencyStore or patch_idempotency_store(app, store) from approck_fastapi_utils.testing.

Response headers:

Header Values
X-Idempotency-Status new for the first successful request, hit for cached replay
X-Idempotency-Signature SHA-256 of method, path, query, and body when signature validation is enabled

Breaking changes in 0.2.0

  • Invalid X-JWT-Payload in ensure_current_user now raises Unauthorized (401) instead of Forbidden (403).
  • decode_payload returns None instead of raising on invalid input.
  • Header alias is explicitly X-JWT-Payload.

Development

This repository uses uv.

uv sync --group dev --extra sqlalchemy
uv run ruff check .
uv run ruff format --check .
uv run mypy approck_fastapi_utils
uv run pytest

License

MIT — see LICENSE.

Contributing

Issues and pull requests are welcome. Please run the checks above before submitting a change.

Metadata

Release files for approck-fastapi-utils 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for approck-fastapi-utils 0.2.1
File Size Uploaded Provenance
approck_fastapi_utils-0.2.1.tar.gz 12.0 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for approck-fastapi-utils 0.2.1
File Interpreter ABI Platform
approck_fastapi_utils-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 30.2 kB

Release files / approck_fastapi_utils-0.2.1.tar.gz

Download URL approck_fastapi_utils-0.2.1.tar.gz
Size 12.0 kB
Tags Source
SHA-256 checksum
How to use checksums
59765b54f7657e634338426a37b444577ab03e17a67ba75a517736f33b037d4c
BLAKE2b-256 checksum
How to use checksums
660a4d73565b4b58e6e746788ebcfd29a8d7c18a7f7b64461897cf35dc65a331
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / approck_fastapi_utils-0.2.1-py3-none-any.whl

Download URL approck_fastapi_utils-0.2.1-py3-none-any.whl
Size 18.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a242741d9ee0e353c3b60982459035e78e8d188fbf1bfc7d6c9deca543414f6d
BLAKE2b-256 checksum
How to use checksums
b09588f26b800c9581b9fc2fdae3273e4e6001e6a0c627fd39bc3d0d3431c5a5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.11.21 {"installer":{"name":"uv","version":"0.11.21","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for approck-fastapi-utils 0.2.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page