REFERENCE
API, no fluff.
Ikarem(debug, enable_docs, version, **config)
app.get/post/put/patch/delete(path) # + .route(path, methods)
app.websocket(path) # WSRouter
app.include_router(router, prefix="")
app.mount_static(url_path, directory)
app.use(middleware) # onion, short-circuitable
app.register(plugin) # deps sorted, cycles rejected
app.on_startup / app.on_shutdown
app.exception_handler(ExcType) # MRO most-specific match
app.run(host, port, reload) # needs ikarem[server]
app.check() app.compile_all()
app.mcp_tools() / mcp_call() / mcp_server()
Routing
/users/{uid:int} — converters str|int|float|uuid|path, pre-compiled.
Static routes resolve O(1). 404 vs 405 distinct. router.url_for(name, **params).
01 / REQUEST · RESPONSE
req.method req.path req.path_params req.query req.headers req.cookies
req.state req.session # with SessionMiddleware
await req.body(max_bytes=None) # streams, capped on demand
await req.json(max_bytes=10MB) # 413 past the cap
await req.form() # urlencoded + multipart, UploadFile
await req.text()
JSONResponse TextResponse HTMLResponse RedirectResponse
StreamingResponse FileResponse(path, media_type, filename, status_code)
resp.set_cookie(...) / resp.delete_cookie(...)
Handlers return dicts/lists/str/bytes, (body, status) tuples, or
Response objects — to_response normalizes. Errors render inside the middleware
pipeline, so 4xx/5xx keep request-ID, security, CORS and rate-limit headers.
02 / DI + SCHEMA
def get_db(req): ...
async def h(req, db=Depends(get_db), bg: BackgroundTasks):
...
class Item(Schema):
name: str = Field(..., min_length=1, max_length=80)
qty: int = Field(1, ge=1, le=999)
email: str = Field(..., email=True)
Depends(): nested, per-request cached (+ opt-out), sync/async/yield, finalizers run
after the response is sent and on the exception path, circular deps rejected at compile time.
Schema validates JSON and form bodies; failures are 400s; constraints flow into
OpenAPI and MCP schemas automatically.
03 / SECURITY
create_token(sub, secret, expires_in, **claims) # HS256, sub required
verify_token(token, secret) # alg-confusion resistant
hash_password / check_password # pbkdf2
claims = Depends(require_roles("admin")) # 401/403
claims = Depends(BearerAuth(secret))
app.use(SessionMiddleware()) # signed cookies: req.session
app.use(CSRFMiddleware()) # X-CSRF-Token header or _csrf_token field
app.use(CORSMiddleware())
app.use(SecurityHeadersMiddleware())
app.use(RateLimitMiddleware(per_minute=120)) # 429 + Retry-After, bounded buckets
Errors
HTTPException → BadRequest Unauthorized Forbidden NotFound
MethodNotAllowed PayloadTooLarge InternalError. Custom handlers via
@app.exception_handler; tracebacks only when debug=True.
04 / OPS
GET /healthz # liveness
GET /readyz # readiness: 503 while the DB is down
GET /metrics # ikarem_requests / errors / uptime
GET /openapi.json GET /docs # OpenAPI 3.1 + Swagger
MemoryCache + @cached (bounded, TTL, Redis-swappable
CacheBackend). DatabasePlugin(url) — SQLite (stdlib) / Postgres /
MySQL / SQLServer strategies, state_db.execute/fetch_one/fetch_all/execute_many/transaction.
05 / CLI + CONFIG
$ ikarem run myapp:app [--host --port --reload]
$ ikarem check myapp:app # gate deploys on this
$ ikarem mcp myapp:app # routes as MCP tools over stdio
$ ikarem new mydir # production starter
Config precedence: defaults < Ikarem(**kwargs) < load_dict <
IKAREM_* env. Typed reads: config.get(key, default, cast=...).