Migration from Express
Ingenium keeps the Express mental model - app.get, app.use, mountable routers, drop-in middleware - but swaps (req, res, next) for a typed ctx and lets handlers return values. The migration cost is intentionally small.
The 5-minute Express to Ingenium diff
// Express // Ingenium
import express from 'express' import { ingenium } from 'ingenium'
const app = express() const app = ingenium()
app.use(express.json()) app.use(ingenium.json()) // (no-op, parsing is lazy)
app.use((req, res, next) => { app.use(async (ctx, next) => {
req.startedAt = Date.now() ctx.state.startedAt = Date.now()
next() await next()
}) })
app.get('/users/:id', (req, res) => { app.get('/users/:id', (ctx) =>
res.json({ id: req.params.id }) ({ id: ctx.params.id }))
})
app.post('/users', (req, res) => { app.post('/users', async (ctx) => {
const body = req.body const body = await ctx.body.json()
// ... // ...
res.status(201).json(user) return ctx.json(user, 201)
}) })
const router = express.Router() const router = ingenium.Router()
router.get('/health', (req, res) => router.get('/health', () => ({ ok: 1 }))
res.json({ok:1}))
app.use('/api', router) app.use('/api', router)
app.use((err, req, res, next) => { app.onError((err, ctx) => {
res.status(500).json({err: err.message}) ctx.json({ err: err.message }, 500)
}) })
app.listen(3000) await app.listen(3000)
Breakable changes
- Handlers may return values.
return objisres.json(obj);return 'text'isres.text(...). Callingctx.json(...)explicitly still works. - Body parsing is lazy.
app.use(ingenium.json())is a no-op stub for ergonomics; the actual parse happens inctx.body.json()inside your handler. ctx.stateis the per-request scratch space, notctx.user = ...directly (though plugins can decoratectxto enable that).
That's the whole list. Everything else from the Express mental model carries over verbatim.
Express middleware that still works
If you depend on existing Express middleware (cors, helmet, cookie-parser, etc.), use the ingenium-compat shim - it wraps (req, res, next) middleware so they run inside the Ingenium chain. The full compatibility matrix lives on that page.
Where to next?
- Core concepts - App, Router, path syntax.
- IngeniumContext - the typed
ctxsurface. - Express compatibility shim - keeping your existing middleware.
- Body parsing - lazy parsers and schema validation.