Web (oridecon-web)
Web layer for Oridecon Framework — ASGI, routing, middleware, and API tooling.
Overview
Section titled “Overview”oridecon-web provides an ASP.NET Core-inspired HTTP layer built on Starlette with constructor injection, a Result-to-HTTP bridge that maps domain errors to status codes automatically, first-class middleware, guard, and filter pipelines, and OpenAPI docs auto-generation.
Full documentation: oridecon.dev
Install
Section titled “Install”uv add oridecon oridecon-web[granian]
# With optional server backendsuv add "oridecon-web[uvicorn]" # uvicornuv add "oridecon-web[hypercorn]" # hypercornuv add "oridecon-web[security]" # itsdangerous for signinguv add "oridecon-web[templates]" # Jinja2 template supportuv add "oridecon-web[websocket]" # WebSocket supportQuick Start
Section titled “Quick Start”from oridecon import Applicationfrom oridecon.di.module import Module, modulefrom oridecon.web import Controller, WebModule, WebProvider, get
class HelloController(Controller): @get("/hello") async def hello(self) -> dict[str, str]: return {"message": "Hello from Oridecon"}
@module( imports=[ WebModule.configure( controllers=[HelloController], host="127.0.0.1", port=8000, ) ])class AppModule(Module): pass
async def main() -> None: async with Application.boot(modules=[AppModule]) as app: web = await app.container.resolve(WebProvider) web.run_server(host="127.0.0.1", port=8000)
if __name__ == "__main__": import asyncio
asyncio.run(main())Configuration
Section titled “Configuration”Zero-config usage: Call
WebModule.configure()with no arguments to use all defaults.
Option 1 — YAML file
Section titled “Option 1 — YAML file”web: server: host: "0.0.0.0" port: 8000 workers: 4 cors: allowed_origins: - "https://app.example.com" rate_limit: enabled: true default_limit: "100/minute"Option 2 — Profiles + Environment Variables (recommended)
Section titled “Option 2 — Profiles + Environment Variables (recommended)”export ORI_WEB__SERVER__HOST=0.0.0.0export ORI_WEB__SERVER__PORT=8080export ORI_WEB__SECURITY__CORS__ALLOWED_ORIGINS='["https://app.example.com"]'Option 3 — Python
Section titled “Option 3 — Python”from oridecon.web import WebModulefrom oridecon.web.config import WebConfig, ServerConfig, RateLimitConfig
WebModule.configure( controllers=[UserController, OrderController], web_config=WebConfig( server=ServerConfig(host="0.0.0.0", port=8080, workers=4), rate_limit=RateLimitConfig( enabled=True, default_limit=200, default_window=60, ), ),)Config reference
Section titled “Config reference”| Field | Default | Env var | Description |
|-------|---------|---------|-------------|
| server.host | "0.0.0.0" | ORI_WEB__SERVER__HOST | Bind host |
| server.port | 8000 | ORI_WEB__SERVER__PORT | Bind port |
| server.workers | 1 | ORI_WEB__SERVER__WORKERS | Worker processes |
| server.reload | False | ORI_WEB__SERVER__RELOAD | Auto-reload on code change |
| cors.allowed_origins | ["http://localhost:3000", "http://localhost:8001"] | ORI_WEB__SECURITY__CORS__ALLOWED_ORIGINS | CORS allow-list — wildcards blocked in production |
| rate_limit.enabled | True | ORI_WEB__RATE_LIMIT__ENABLED | Enable rate limiting |
| rate_limit.default_limit | 100 | ORI_WEB__RATE_LIMIT__DEFAULT_LIMIT | Requests per window |
| rate_limit.default_window | 60 | ORI_WEB__RATE_LIMIT__DEFAULT_WINDOW | Window in seconds |
| rate_limit.storage_backend | "memory" | ORI_WEB__RATE_LIMIT__STORAGE_BACKEND | "memory" or "redis" |
| enable_auth | False | ORI_WEB__ENABLE_AUTH | Enable built-in auth middleware |
| api_docs.enabled | True | ORI_WEB__API_DOCS__ENABLED | Enable /docs + /redoc |
| max_body_size | 10 MiB | ORI_WEB__MAX_BODY_SIZE | Request body size limit |
Module Factory Methods
Section titled “Module Factory Methods”| Method | Description |
|--------|-------------|
| WebModule.configure(controllers, discover, ...) | Configure with controllers and server settings |
| WebModule.stub() | No-op module for unit testing |
Key Features
Section titled “Key Features”- Controller pattern — subclass
Controllerand annotate methods with HTTP decorators - Result-to-HTTP bridge —
Result[T, DomainError]maps automatically to status codes (404, 422, 403, etc.) - HTTP decorators —
@get,@post,@put,@delete,@patch,@websocket, etc. - Auto-discovery —
WebModule.configure(discover=["my_app.api.v1"]) - Middleware pipeline — register ASGI middleware via
MiddlewareRegistry - Exception filters —
DefaultExceptionFilterhandlesDomainErrorandHTTPErrorglobally - Static files, API docs, debug routes — configurable via
WebConfig - Rate limiting — per-path rules with memory or Redis storage backend
- Security — CORS wildcard blocked in production, CSRF enabled by default
Testing
Section titled “Testing”from oridecon import Applicationfrom oridecon.web import WebModule
async def test_controller(): async with Application.boot(modules=[WebModule.stub()]) as app: web = await app.container.resolve(WebProvider) assert web.starlette is not NoneKey Source Files
Section titled “Key Source Files”| File | What it contains |
|------|----------------|
| src/oridecon/web/module.py | WebModule.configure() |
| src/oridecon/web/di/provider.py | WebProvider boot phases |
| src/oridecon/web/routing/decorators.py | HTTP decorators (@get, @post, etc.) |
| src/oridecon/web/routing/result_bridge.py | ResultResponseMapper for Result-to-HTTP mapping |
| src/oridecon/web/config.py | WebConfig, ServerConfig, RateLimitConfig |
| src/oridecon/web/middleware/__init__.py | ASGI middleware classes + MiddlewareRegistry |
| src/oridecon/web/filters/__init__.py | Exception filters |