Skip to content

Framework-Modulreferenz

Öffentliche API des nene2-Pakets.


nene2.http

PaginationQueryParser

Parst limit- und offset-Query-Parameter.

FastAPI Depends (empfohlen):

python
from typing import Annotated
from fastapi import Depends
from nene2.http import PaginationQueryParser

@router.get("/items")
def list_items(pagination: Annotated[PaginationQueryParser, Depends()]) -> JSONResponse:
    result = use_case.execute(pagination.limit, pagination.offset)

Legacy (Request-basiert):

python
from nene2.http import PaginationQueryParser

pagination = PaginationQueryParser.parse(request)
# pagination.limit  → int (max 100, Standard 20)
# pagination.offset → int (Standard 0)

PaginationResponse

Umschließt eine paginierte Ergebnismenge.

python
from nene2.http import PaginationResponse

body = PaginationResponse(items=[...], limit=20, offset=0, total=42).to_dict()
# → {"items": [...], "limit": 20, "offset": 0, "total": 42}

problem_details_response()

Generiert eine RFC-9457-Problem-Details-Antwort.

python
from nene2.http import problem_details_response

return problem_details_response("not-found", "Not Found", 404, "Note 42 not found.")

PaginationQuery

Dataclass, der von PaginationQueryParser.parse() zurückgegeben wird. Enthält limit: int und offset: int.

HealthCheckProtocol / HealthStatus

Vertrag und Ergebnistyp für Anwendungs-Health-Checks.

python
from nene2.http import HealthCheckProtocol, HealthStatus

class MyHealthCheck:
    def check(self) -> HealthStatus:
        return HealthStatus(status="ok")

HealthStatus-Felder: status: str ("ok" oder "error"), checks: dict[str, str]. Die is_healthy-Property gibt True zurück, wenn status == "ok".

ETag und bedingte Anfragen

python
from nene2.http import check_not_modified, check_precondition, generate_etag

etag = generate_etag({"id": 1, "title": "Hello"})
# Gibt 304 zurück, wenn If-None-Match übereinstimmt (GET)
check_not_modified(request, etag)
# Gibt 412 zurück, wenn If-Match nicht übereinstimmt (PUT/PATCH/DELETE)
check_precondition(request, etag)

Query-Parameter-Helfer

Typisierte Parser für gängige Query-Muster (lösen ValidationException bei ungültiger Eingabe aus):

python
from nene2.http import query_array, query_bool, query_comma_separated, query_int, query_string

limit = query_int(request, "limit", default=20, minimum=1, maximum=100)
tags = query_comma_separated(request, "tags", max_items=10)

RequestScopedContext[T]

Request-scoped Werthalter für Dependency Injection (siehe lifespan-and-app-state).

PaginationDep

FastAPI-Depends()-Alias für PaginationQueryParser — gegenüber manuellem Parsing bevorzugt.


nene2.use_case

UseCaseProtocol[I, O]

Struktureller Vertrag für synchrone UseCases.

python
from nene2.use_case import UseCaseProtocol

class MyUseCase:
    def execute(self, input_: MyInput) -> MyOutput: ...

assert isinstance(MyUseCase(), UseCaseProtocol)

AsyncUseCaseProtocol[I, O]

Struktureller Vertrag für asynchrone UseCases.

python
from nene2.use_case import AsyncUseCaseProtocol

class MyAsyncUseCase:
    async def execute(self, input_: MyInput) -> MyOutput: ...

assert isinstance(MyAsyncUseCase(), AsyncUseCaseProtocol)

Hinweis: isinstance prüft nur das Vorhandensein von Attributen. Die async/sync-Unterscheidung wird statisch durch mypy --strict erzwungen.


nene2.config

AppSettings

Pydantic-Settings-Klasse — liest aus Umgebungsvariablen und .env.

python
from nene2.config import AppSettings

cfg = AppSettings()                                   # aus der Umgebung
cfg_test = AppSettings(throttle_enabled=False)        # Überschreibung für Tests

Alle Felder finden Sie in der Konfigurationsreferenz.


nene2.middleware

ErrorHandlerMiddleware

Fängt alle unbehandelten Ausnahmen ab und wandelt sie in Problem-Details-Antworten um. Registrieren Sie Domain-Ausnahme-Handler über DomainExceptionHandlerProtocol.

python
from starlette.responses import Response
from nene2.http import problem_details_response
from nene2.middleware import ErrorHandlerMiddleware
from nene2.middleware.domain_exception import DomainExceptionHandlerProtocol

class NoteNotFoundExceptionHandler:
    def handles(self, exc: Exception) -> bool:
        return isinstance(exc, NoteNotFoundException)

    def handle(self, exc: Exception) -> Response:
        assert isinstance(exc, NoteNotFoundException)
        return problem_details_response("not-found", "Not Found", 404, str(exc))

# Registrierung — als domain_handlers-Liste übergeben:
app.add_middleware(
    ErrorHandlerMiddleware,
    debug=settings.app_debug,
    domain_handlers=[NoteNotFoundExceptionHandler()],
)

DomainExceptionHandlerProtocol erfordert zwei Methoden:

MethodeSignaturZweck
handles(exc: Exception) -> boolTrue zurückgeben, wenn dieser Handler die Ausnahme besitzt
handle(exc: Exception) -> ResponseAusnahme in eine HTTP-Antwort umwandeln

Andere Middleware

KlasseModulRolle
SecurityHeadersMiddlewarenene2.middleware.security_headersSicherheits-Response-Header hinzufügen
RequestIdMiddlewarenene2.middleware.request_idX-Request-ID generieren / weitergeben
RequestLoggingMiddlewarenene2.middleware.request_loggingStrukturiertes Anfrage-/Antwort-Logging
RequestSizeLimitMiddlewarenene2.middleware.request_size_limitZu große Request-Bodies ablehnen
ThrottleMiddlewarenene2.middleware.throttleFestfenster-Rate-Limiting pro IP

add_middleware-Argumente

Starlette wendet Middleware in umgekehrter Registrierungsreihenfolge an — die zuletzt registrierte wird zur äußersten Schicht. Registrieren Sie ErrorHandlerMiddleware zuerst, damit sie alle Ausnahmen von jeder anderen Middleware abfängt.

MiddlewareKeyword-ArgumenteStandard
ErrorHandlerMiddlewaredebug: bool, domain_handlers: list[DomainExceptionHandlerProtocol] | NoneFalse, None
SecurityHeadersMiddleware(keine)
RequestIdMiddleware(keine)
RequestLoggingMiddleware(keine)
RequestSizeLimitMiddlewaremax_bytes: int1_048_576 (1 MiB)
ThrottleMiddlewarelimit: int, window: int60, 60

ThrottleMiddleware hat kein enabled-Flag — verwenden Sie if settings.throttle_enabled: zum Deaktivieren.

Hinweis — X-Forwarded-For-Spoofing: Der Rate-Limit-Schlüssel wird aus dem ersten Eintrag des X-Forwarded-For-Headers abgeleitet, den Clients fälschen können. In der Produktion platzieren Sie die Anwendung immer hinter einem vertrauenswürdigen Reverse-Proxy (nginx, Caddy, AWS ALB, usw.), der X-Forwarded-For umschreibt, bevor die Anfrage die App erreicht. Einzelheiten finden Sie in ADR-0006.

Vollständige Registrierungsreihenfolge mit optionaler Middleware

python
# Registrierungsreihenfolge: innerste zuerst, äußerste zuletzt.
# Starlette führt in umgekehrter Reihenfolge aus — die zuletzt registrierte umschließt alle anderen.
app.add_middleware(ErrorHandlerMiddleware, debug=settings.app_debug, domain_handlers=[...])
app.add_middleware(SecurityHeadersMiddleware)
app.add_middleware(RequestIdMiddleware)
app.add_middleware(RequestLoggingMiddleware)
app.add_middleware(RequestSizeLimitMiddleware, max_bytes=settings.max_body_size)
if settings.throttle_enabled:
    app.add_middleware(ThrottleMiddleware, limit=settings.throttle_limit, window=settings.throttle_window)
# Auth-Middleware — vor CORS registriert, damit sie innerhalb der CORS-Schicht sitzt
if settings.bearer_token_enabled:
    app.add_middleware(BearerTokenMiddleware, verifier=LocalTokenVerifier(settings.bearer_tokens))
if settings.api_key_enabled:
    app.add_middleware(ApiKeyAuthMiddleware, verifier=LocalTokenVerifier(settings.api_keys))
# CORS muss die äußerste Schicht sein — zuletzt registrieren.
# OPTIONS-Preflight-Anfragen müssen CORSMiddleware vor jeder Auth-Prüfung erreichen.
# Wenn CORSMiddleware vor Auth-Middleware registriert wird, wird die Auth-Schicht
# äußerste und gibt 401 bei Preflight zurück, was CORS für alle Browser bricht.
if settings.cors_enabled:
    app.add_middleware(
        CORSMiddleware,
        allow_origins=settings.cors_origins,
        allow_credentials=settings.cors_allow_credentials,
        allow_methods=settings.cors_allow_methods,
        allow_headers=settings.cors_allow_headers,
    )

CORS + Auth-Regel: Registrieren Sie CORSMiddleware immer nach Auth-Middleware. In Starletttes umgekehrter Reihenfolge bedeutet "zuletzt registriert = äußerste", dass CORS Auth umschließt, sodass Browser-Preflight-Anfragen (OPTIONS) vor der Authentifizierung behandelt werden.

setup_middlewares()

Registriert den vollständigen nene2-Middleware-Stack in der korrekten LIFO-Reihenfolge (einschließlich optionalem CORS). Bevorzugen Sie dies gegenüber manuellen add_middleware-Aufrufen, wenn Sie keine benutzerdefinierte Middleware benötigen.

python
from nene2.middleware import setup_middlewares

setup_middlewares(
    app,
    debug=settings.app_debug,
    domain_handlers=[NoteNotFoundExceptionHandler()],
    throttle_limit=settings.throttle_limit if settings.throttle_enabled else None,
    max_request_bytes=settings.max_body_size,
    cors_allowed_origins=settings.cors_origins if settings.cors_enabled else None,
)

Siehe middleware-stack How-to.

SimpleDomainHandler

Helfer zum Aufbauen von DomainExceptionHandlerProtocol aus einem Ausnahmetyp und Statuscode.

Rate-Limit-Speicher

SymbolRolle
RateLimitStorageProtocolAustauschbarer Speicher für Throttle-Zähler
InMemoryRateLimitStorageStandard-In-Process-Implementierung
ThrottleMiddlewareAkzeptiert optionales storage= für benutzerdefinierte Backends

nene2.auth

LocalTokenVerifier

Verifiziert Tokens gegen eine statische Liste mit secrets.compare_digest.

python
from nene2.auth import LocalTokenVerifier

verifier = LocalTokenVerifier(["token-a", "token-b"])
verifier.verify("token-a")  # True
verifier.verify("wrong")    # False

TokenVerifierProtocol / TokenIssuerProtocol

Strukturelle Verträge für benutzerdefinierte Verifizierer und Aussteller (z. B. JWT).

TokenVerificationException

Lösen Sie dies aus einem Verifizierer aus, um einen ungültigen Token zu signalisieren. BearerTokenMiddleware ordnet es 401 Unauthorized zu.

CompositeAuthMiddleware

Pfad-Präfix-Regeln für gemischte Auth (z. B. Bearer auf /api/*, API-Key auf /internal/*).

python
from nene2.auth import CompositeAuthMiddleware, CompositeAuthRule, bearer_check, api_key_check

app.add_middleware(
    CompositeAuthMiddleware,
    rules=[
        CompositeAuthRule(prefix="/api", check=bearer_check(verifier)),
        CompositeAuthRule(prefix="/internal", check=api_key_check(verifier)),
    ],
)

LocalTokenIssuer / LocalBearerJwtVerifier

Entwicklungshelfer für HMAC-signierte Bearer-Tokens (siehe geschützte Routen in src/example/).

make_require_auth()

FastAPI-Depends()-Factory, die 401 Problem Details zurückgibt, wenn Auth-Header fehlen.


nene2.database

SqlAlchemyQueryExecutor

Führt parametrisiertes SQL über SQLAlchemy Core aus.

python
from nene2.database import SqlAlchemyQueryExecutor

executor = SqlAlchemyQueryExecutor(engine)
rows = executor.fetch_all("SELECT * FROM notes WHERE id = :id", {"id": 1})
executor.write("INSERT INTO notes (title, body) VALUES (:t, :b)", {"t": "t", "b": "b"})

Rückgabewert von write()

write() gibt ein int zurück, dessen Bedeutung von der SQL-Operation abhängt:

OperationRückgabewert
INSERT mit AUTOINCREMENT / SERIALlastrowid — der Primärschlüssel der neuen Zeile (immer > 0)
INSERT ohne Auto-PK oder Multi-Row-INSERTrowcount — Anzahl der eingefügten Zeilen
UPDATE / DELETErowcount — betroffene Zeilen (0 wenn nichts zutraf)

SqlAlchemyTransactionManager

Verwaltet Transaktionen. Bevorzugen Sie transactional() gegenüber manuellen begin/commit/rollback-Aufrufen.

python
from nene2.database import SqlAlchemyTransactionManager

mgr = SqlAlchemyTransactionManager(engine)

result = mgr.transactional(
    lambda ex: ex.fetch_one("SELECT COUNT(*) AS cnt FROM notes")
)

transactional() verwendet intern engine.begin() — jede Ausnahme innerhalb des Callbacks löst einen automatischen Rollback aus.

DatabaseHealthCheck

Implementiert HealthCheckProtocol — verifiziert die Datenbankverbindung und gibt einen HealthStatus zurück.

python
from nene2.database import DatabaseHealthCheck
from nene2.http import HealthStatus

health = DatabaseHealthCheck(engine)
status: HealthStatus = health.check()
# status.status → "ok" or "error"
# status.checks → {"db": "ok"} or {"db": "error: <message>"}

DatabaseConnectionException

Wird von DatabaseHealthCheck oder Repository-Operationen ausgelöst, wenn die Datenbank nicht erreichbar ist.


nene2.mcp

LocalMcpServer

Umschließt FastMCP — registriert UseCase-Funktionen als MCP-Tools.

python
from nene2.mcp import LocalMcpServer

server = LocalMcpServer("my-server", instructions="...")

@server.tool("List all notes.")
def list_notes(limit: int = 20, offset: int = 0) -> list[dict]: ...

server.run(transport="stdio")

HttpxMcpClient

HTTP-Client zum Aufrufen einer nene2-API aus MCP-Tool-Handlern.

python
from nene2.mcp import HttpxMcpClient

client = HttpxMcpClient("bearer-token")
response = client.get("http://localhost:8080", "/notes")
response.is_successful()   # True
response.body              # str — roher Antworttext
response.status_code       # int
response.request_id()      # str | None — Wert des X-Request-ID-Headers

McpHttpResponse

Rückgabetyp von HttpxMcpClient-Methoden.

Felder: status_code: int, headers: dict[str, str], body: str (roher Antworttext).

Methoden:

  • is_successful() -> boolTrue wenn 200 ≤ status_code < 300
  • request_id() -> str | None — gibt den X-Request-ID-Antwort-Header-Wert zurück, oder None

McpHttpClientProtocol

Struktureller Vertrag für benutzerdefinierte MCP-HTTP-Clients. Implementieren Sie get(), post(), put(), delete() mit Rückgabe von McpHttpResponse und has_authentication() -> bool.


nene2.log

setup_logging()

Initialisiert structlog. Wechselt zwischen ConsoleRenderer (lokal) und JSON (Produktion).

python
from nene2.log import setup_logging

setup_logging(app_env="production")  # JSON-Renderer
setup_logging(app_env="local")       # Konsolen-Renderer

nene2.validation

ValidationException / ValidationError

Lösen Sie ValidationException an der HTTP-Grenze aus, um 422 Unprocessable Entity zurückzugeben.

python
from nene2.validation.exceptions import ValidationError, ValidationException

errors = [ValidationError("body", "Body must not be empty.", "required")]
raise ValidationException(errors)

nene2.cache

TtlCache[V]

Thread-sicherer In-Memory-Cache mit TTL-Ablauf pro Schlüssel. Verwenden Sie ihn für Idempotenz-Schlüssel, kurzlebige Lookups oder Rate-Limit-Ergänzungen.

python
from nene2.cache import TtlCache

cache: TtlCache[str] = TtlCache(ttl_seconds=60.0)
cache.set("key", "value")
cache.get("key")  # str | None

Siehe lifespan-and-app-state How-to für die app.state-Verdrahtung.


nene2.security

verify_hmac_signature()

Zeitkonstante HMAC-Verifizierung für Webhook-Endpunkte.

python
from nene2.security import verify_hmac_signature

if not verify_hmac_signature(body, signature_header, secret.get_secret_value()):
    return problem_details_response("unauthorized", "Unauthorized", 401, "Invalid signature.")

Siehe webhook How-to.

Veröffentlicht unter der MIT-Lizenz.