Skip to main content

Small helpers for FastAPI: JWT header auth, JSON responses, exception handlers, and Pydantic query types.

Project description

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.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

approck_fastapi_utils-0.2.1.tar.gz (12.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

approck_fastapi_utils-0.2.1-py3-none-any.whl (18.2 kB view details)

Uploaded Python 3

File details

Details for the file approck_fastapi_utils-0.2.1.tar.gz.

File metadata

  • Download URL: approck_fastapi_utils-0.2.1.tar.gz
  • Upload date:
  • Size: 12.0 kB
  • Tags: Source
  • Uploaded using 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}

File hashes

Hashes for approck_fastapi_utils-0.2.1.tar.gz
Algorithm Hash digest
SHA256 59765b54f7657e634338426a37b444577ab03e17a67ba75a517736f33b037d4c
MD5 449092c8eb6fc8bf686c4b365d894e96
BLAKE2b-256 660a4d73565b4b58e6e746788ebcfd29a8d7c18a7f7b64461897cf35dc65a331

See more details on using hashes here.

File details

Details for the file approck_fastapi_utils-0.2.1-py3-none-any.whl.

File metadata

  • Download URL: approck_fastapi_utils-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 18.2 kB
  • Tags: Python 3
  • Uploaded using 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}

File hashes

Hashes for approck_fastapi_utils-0.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 a242741d9ee0e353c3b60982459035e78e8d188fbf1bfc7d6c9deca543414f6d
MD5 81d461c563077f4f85c3ab6a48ca9e83
BLAKE2b-256 b09588f26b800c9581b9fc2fdae3273e4e6001e6a0c627fd39bc3d0d3431c5a5

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page