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-Payloadinensure_current_usernow raisesUnauthorized(401) instead ofForbidden(403). decode_payloadreturnsNoneinstead 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)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| approck_fastapi_utils-0.2.1.tar.gz | 12.0 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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.