IKAREMIKAREM v1.1.0

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=...).