Skip to main content

An ASGI web framework with dependency injection, metadata-driven routing, and modular architecture.

Project description

lauren

lauren framework: a metadata-first Python web framework. NestJS-style modules, Axum-style extractors, FastAPI-class ergonomics — resolved into an immutable execution graph at startup.

Test Lint CodeQL Coverage Package version Supported Python versions Downloads License Ruff Checked with mypy prek Discussions GitHub Stars


Documentation: https://lauren-py.dev

Source Code: https://github.com/lauren-framework/lauren-framework


For AI Agents & Coding Assistants

Install all skills in one command

# Claude Code, Cursor, Copilot, Continue, Codex CLI — auto-detected
npx skills add lauren-framework/lauren-framework

This copies all 60+ SKILL.md context packs into your agent's global skills directory (~/.claude/skills/, ~/.cursor/skills/, etc.). The next time your agent opens a Lauren project it has pre-loaded expertise on auth, database, API patterns, observability, security, and more.

Resource What it contains
llms.txt 2 KB framework overview — start here
lauren/llms-full.txt 25 KB complete reference — all APIs, patterns, common errors
AGENTS.md Agent rules, by-task lookup, file ownership, common errors, definition of done
CLAUDE.md Conventions, commands, golden rules, pattern selection guide
skills/ 60+ copy-paste skill guides covering every common task

Full install guide and skills index: docs/guides/agent-skills.md


lauren is a modern, high-performance Python web framework for building APIs that need to fail at startup, not in production. It is built on these core ideas:

  • Metadata-first. Routes, dependency-injection bindings, module boundaries, lifecycle hooks, middleware, and guards are declared with decorators that attach metadata. They never rewrite your functions.
  • Startup-validated, runtime-pure. Every misuse — circular DI, missing module export, malformed extractor, conflicting routes — is rejected inside LaurenFactory.create(...), not on the first request.
  • No reflection on the request path. The whole graph is compiled into immutable structures at startup; serving a request is pure traversal.
  • AI-ready by default. Public surface is mirrored in llms.txt / llms-full.txt and exported in __all__ — a CI hook keeps the two in lock-step.

Features

  • Fast — Zero per-request reflection: routes, DI graph, extractors, and middleware are fully compiled at startup.
  • Implicit extractors — Path params, query strings, and JSON bodies are auto-detected from type annotations. Write id: int, q: str, body: MyModel without boilerplate unless you need the explicit form.
  • Three-scope DISINGLETON, REQUEST, and TRANSIENT scopes with Protocol bindings, multi-bindings (list[T]), and NestJS-style custom providers (use_value / use_class / use_factory / use_existing).
  • Pipes — Post-extraction value transforms: validate, coerce, or enrich parameters before they reach the handler. Function-based, class-based, chainable, and DI-aware.
  • Guards, Middleware & Interceptors — All three attachment points. Guards run first (allow/deny); middleware wraps raw request/response bytes; interceptors wrap handler execution for timing, caching, and response transforms.
  • WebSockets — First-class @ws_controller gateways with typed Pydantic frames, discriminated-union dispatch, and BroadcastGroup rooms.
  • Server-Sent EventsEventStream with keep-alive heartbeats and Last-Event-ID resumability for AI token-streaming patterns.
  • Typed streamingStreamingResponse[T] auto-negotiates between SSE, NDJSON, and JSON Lines based on the client's Accept header.
  • Background tasksBackgroundTasks extractor fires work after the response is sent. TaskHandle exposes cancel/await. Signals notify on start, complete, and failure.
  • Static filesStaticFilesModule.for_root("/assets", directory="./public") with ETag caching, Cache-Control, and path-traversal protection.
  • Socket.IO — Engine.IO v4 / Socket.IO v5 adapter via @socketio_controller.
  • OpenAPI 3.1 — Automatic schema generation from Pydantic models and route decorators. Swagger UI and ReDoc served out of the box.
  • Lifecycle signalsSignalBus with StartupBegin, StartupComplete, ShutdownBegin, RequestReceived, RequestComplete, and more.
  • Standards-based — Built on ASGI, Pydantic, and anyio.

Requirements

Python 3.11, 3.12, 3.13, and 3.14 are supported. Hard dependencies:

  • Pydantic — request validation and response serialisation.
  • anyio — async backend and thread-pool offload for sync handlers.

Installation

pip install lauren
# with an ASGI server:
pip install "uvicorn[standard]"
# or granian (faster on CPython):
pip install granian

Quick start

from pydantic import BaseModel
from lauren import LaurenFactory, controller, get, post, module, use_guards
from lauren.types import ExecutionContext


class CreateUser(BaseModel):
    name: str
    age: int


class AdminGuard:
    async def can_activate(self, ctx: ExecutionContext) -> bool:
        return ctx.request.headers.get("x-role") == "admin"


@controller("/users", tags=["users"])
class UserController:
    @get("/{id}")
    async def get_user(self, id: int) -> dict:
        return {"id": id, "found": True}

    @post("/")
    async def create(self, body: CreateUser):
        return body.model_dump(), 201

    @get("/admin")
    @use_guards(AdminGuard)
    async def admin_only(self) -> dict:
        return {"access": "granted"}


@module(controllers=[UserController])
class AppModule:
    pass


app = LaurenFactory.create(AppModule, docs_url="/docs")
uvicorn main:app --reload
# → http://127.0.0.1:8000/docs  (Swagger UI)

Dependency injection

from lauren import injectable, post_construct, pre_destruct, Scope

@injectable(scope=Scope.SINGLETON)
class UserRepository:
    @post_construct
    async def warm(self) -> None:
        self.cache: dict[int, str] = {}

    @pre_destruct
    async def flush(self) -> None:
        self.cache.clear()

    async def find(self, user_id: int) -> str | None:
        return self.cache.get(user_id)

    async def upsert(self, user_id: int, name: str) -> None:
        self.cache[user_id] = name


@controller("/users")
class UserController:
    def __init__(self, repo: UserRepository) -> None:
        self.repo = repo

    @get("/{id}")
    async def get_user(self, id: int) -> dict:
        name = await self.repo.find(id)
        if name is None:
            return {"id": id, "found": False}, 404
        return {"id": id, "name": name}


@module(providers=[UserRepository], controllers=[UserController])
class AppModule:
    pass

Modules

Modules are the unit of feature composition. Each module declares what it provides, what it exports, and what it imports from other modules — similar to NestJS:

@module(
    imports=[DatabaseModule, AuthModule],
    controllers=[UserController, ProfileController],
    providers=[UserService, EmailService],
    exports=[UserService],
)
class UsersModule:
    pass

Circular dependency detection, missing export errors, and scope violations are all caught at startup — before your first request.

SSE / streaming

from lauren import EventStream, ServerSentEvent, get

@get("/events")
async def stream(self) -> EventStream:
    async def generate():
        for i in range(10):
            yield ServerSentEvent(data=f"tick {i}", event="tick")
            await asyncio.sleep(1)
        yield ServerSentEvent(data="done", event="close")
    return EventStream(generate(), keep_alive=15.0)

For typed, content-negotiated streams (SSE / NDJSON / JSON Lines):

from pydantic import BaseModel
from lauren import StreamingResponse

class Tick(BaseModel):
    seq: int

@get("/ticks")
async def ticks(self) -> StreamingResponse[Tick]:
    async def gen():
        for i in range(100):
            yield Tick(seq=i)
            await asyncio.sleep(0.05)
    return StreamingResponse(gen())

Static files

from lauren.static_files import StaticFilesModule

@module(imports=[StaticFilesModule.for_root("/assets", directory="./public")])
class AppModule:
    pass

Or mount any ASGI sub-app:

app = LaurenFactory.create(AppModule)
app.mount("/static", StaticFiles(directory="static"))

Performance

Runtime is pure traversal of pre-compiled structures — no inspect.signature(...), no get_type_hints(...), no isinstance(...) walking on the hot path. The DI graph, route table, extractor bindings, and middleware pipeline are all resolved once at startup. Each request pays only the cost of dispatching through the already-compiled graph.

Optional dependencies

Package Purpose
uvicorn / hypercorn / granian ASGI server (none bundled — pick one)
httpx Required for lauren.testing.TestClient
orjson Faster JSON — auto-detected at import time
msgspec Alternative fast JSON encoder via MsgspecEncoder
python-multipart Required for Form[...] extractors

Companion packages

Package Purpose
lauren-middlewares CORS, rate-limit, GZip, security headers, request-id, trusted hosts, HTTPS redirect, body-size limit, timeout
lauren-logging Structured logging module with processor pipeline, contextvars binding, pluggable backends (stdlib, structlog, file, fan-out)
lauren-guards Auth guards: JWT bearer, API key, basic auth, OAuth2 introspection, session cookie, RBAC/ABAC, CSRF, IP allowlist

Deployment

lauren is a standard ASGI application — deploy exactly like FastAPI or Starlette:

# Uvicorn
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4

# Granian (Rust-based, faster on CPython)
granian --interface asgi main:app --host 0.0.0.0 --port 8000

# Hypercorn (HTTP/2 + HTTP/3)
hypercorn main:app --bind 0.0.0.0:8000

Contributing

We welcome contributions of every size, from typo fixes to whole subsystems. Read first:

  1. CONTRIBUTING.md — setup, branch & commit conventions, and the quality bar.
  2. AGENTS.md — the design invariants every PR must respect, whether the author is human or an AI agent.
uv tool install prek      # one-time
prek install              # wires up the git hook
nox                       # lint + tests + typecheck

License

MIT — see LICENSE.

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

lauren-1.0.0.tar.gz (1.1 MB view details)

Uploaded Source

Built Distribution

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

lauren-1.0.0-py3-none-any.whl (249.7 kB view details)

Uploaded Python 3

File details

Details for the file lauren-1.0.0.tar.gz.

File metadata

  • Download URL: lauren-1.0.0.tar.gz
  • Upload date:
  • Size: 1.1 MB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for lauren-1.0.0.tar.gz
Algorithm Hash digest
SHA256 f0839576669b7ff86070e11c64bc4ef564add82ee5cf7da77cdfcca3dce734f2
MD5 6b6924afb928242d71951055c0f90431
BLAKE2b-256 0a9dac410c022f6ee2f701fd0a514a8affc28edb80ceddab2b16edeeda1f8010

See more details on using hashes here.

Provenance

The following attestation bundles were made for lauren-1.0.0.tar.gz:

Publisher: release.yml on lauren-framework/lauren-framework

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file lauren-1.0.0-py3-none-any.whl.

File metadata

  • Download URL: lauren-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 249.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for lauren-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 091cb0ce04e7c7e066fef3703adcd694c56cab6b4cafe13a81b7b503e5bb0823
MD5 3379fbb9388353426071bfc202e1055a
BLAKE2b-256 cb43a214b1203be110485967df0c61014392e9ded02a10018b3b47414290bb46

See more details on using hashes here.

Provenance

The following attestation bundles were made for lauren-1.0.0-py3-none-any.whl:

Publisher: release.yml on lauren-framework/lauren-framework

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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