MIGRATE / FROM ANYWHERE
Two lines for Meraki.
A day for the rest.
Pick your framework below — every guide has a mapping table,
the honest deltas, and a plan. Full guides live in the repo under
docs/MIGRATING_FROM_*.md.
$ pip uninstall meraki -y && pip install ikarem
- from meraki import Meraki
+ from ikarem.meraki_compat import Meraki
That's the whole migration. Same decorators, same add_middleware, same 404/405
plain-text bodies, same byte-pair headers, same Response(body, status_code).
Your uvicorn example:app invocation keeps working. Run your test suite — it passes
unchanged.
01 / API MAP
Same shape. More underneath.
| Meraki | IKAREM native |
|---|---|
Response(body=b"…", status_code=201) | Same shape — or just return {...}, 201 |
request.path, request.method | Same, plus query, cookies, path_params, await body()/json()/form() |
request.query_params (tuples) | request.query (dict) |
request.headers (byte pairs) | request.headers (lowercased dict) |
| Static paths only | {uid:int/float/uuid/path}, 404 vs 405, url_for |
pipeline.add(mw) | app.use(...): CORS, security headers, rate limit, sessions, CSRF |
plugins/ (empty), config/ (empty) | Working plugins + layered Config + DatabasePlugin |
Request never stores receive, so it
cannot read POST bodies at all. Any endpoint reading a body is new capability, not ported code.02 / CASH IN
Highest ROI first.
1. Path params. Meraki has none — GET /users/{uid:int} starts working the
moment you use the compat layer (request.ikarem.path_params inside old handlers).
2. Return dicts. Replace Response(body=json.dumps(...).encode()) with
return {...}.
3. Kill validation boilerplate with Schema + Field() — invalid
bodies become 400s and document themselves in OpenAPI.
4. Run ikarem check myapp:app — circular deps and bad Depends()
surface before traffic does.
03 / GO NATIVE
One file at a time.
app = Meraki().ikarem # the full Ikarem app under your compat app
Meraki().ikarem exposes the engine: include_router,
exception_handler, mount_static, websocket,
mcp_tools(). Both styles coexist on one app. You're done when
grep -r meraki_compat is empty. Worked example: the
Ledger app was built exactly this way.
04 / FROM FASTAPI
A day. Mostly mechanical.
| FastAPI | IKAREM |
|---|---|
@app.get("/users/{uid}") + uid: int | @app.get("/users/{uid:int}") + uid: int |
class Item(BaseModel) | class Item(Schema) + Field() |
x=Depends(fn) | x=Depends(fn) — same spelling |
BackgroundTasks | BackgroundTasks — same name |
TestClient, /docs, /openapi.json | Same names, same paths |
Converters move into the route, there is no response_model filtering,
and advanced Pydantic types become plain fields plus handler checks.
Full guide: docs/MIGRATING_FROM_FASTAPI.md.
05 / FROM STARLETTE
Hours. The closest cousin.
| Starlette | IKAREM |
|---|---|
Route("/users/{uid:int}", endpoint) | @app.get("/users/{uid:int}") |
JSONResponse, PlainTextResponse | Same names |
Middleware(...) | app.use(mw) |
StaticFiles | app.mount_static(url, dir) |
TestClient, lifespan | TestClient, on_startup/shutdown |
Swap the router file, keep handler bodies, then add Schema bodies where
you used to parse await request.json() by hand.
Full guide: docs/MIGRATING_FROM_STARLETTE.md.
06 / FROM LITESTAR
A day. Controller by controller.
| Litestar | IKAREM |
|---|---|
Controller with path= | Blueprint(name, url_prefix=) |
Provide(dep) | Depends(dep) |
| DTOs | Schema + Field() |
guards=[...] | require_roles() / require_scopes() |
(data, StatusCode.CREATED_201) | (data, 201) |
Keep paths identical per controller, diff the 400s, replay the auth tests. Full guide: docs/MIGRATING_FROM_LITESTAR.md.
07 / FROM FLASK
A day. Half the API already crossed over.
| Flask | IKAREM |
|---|---|
@app.route("/x", methods=["POST"]) | @app.post("/x") |
Global request proxy | Explicit req param — no magic |
jsonify, abort, flash | return {...}, abort, flash — same names |
Blueprint, MethodView | Same names, full DI per method |
render_template | Templates(dir) (ikarem[jinja]) |
The real work is globals → req and sync → async (blocking DB drivers
first). Blueprint + MethodView + abort + flash
already shipped as Flask takes.
Full guide: docs/MIGRATING_FROM_FLASK.md.
08 / FROM DJANGO
A week. A rewrite of views, stated plainly.
| Django | IKAREM |
|---|---|
urls.py tree | Routes + include_router(prefix) |
| Models + ORM | DatabaseConnector strategy + Schema (SQL stays yours) |
contrib.auth + sessions | JWT + signed-cookie sessions + CSRF |
MIDDLEWARE setting | app.use(...) order, fail-fast at boot |
manage.py | ikarem run|check|mcp|new|migrate|worker|inspect |
No ORM and no admin are standing decisions, not gaps. Freeze the URL tree, port
models to SQL with the suite green between, cut over one subtree at a time behind
mount_asgi().
Full guide: docs/MIGRATING_FROM_DJANGO.md.