Express compatibility shim
The ingenium-compat package wraps (req, res, next) middleware so it can run inside a Ingenium middleware chain. The shims are real Node streams - req extends stream.Readable, res extends stream.Writable (and is therefore a real EventEmitter) - wired directly to the IngeniumContext. That makes Express middleware a genuine drop-in: body-reading middleware (body-parser, multer) and response-transforming middleware (compression, express-session) all work end-to-end.
Install
npm install ingenium ingenium-compat cors helmet
Usage
import { ingenium } from 'ingenium'
import { expressCompat } from 'ingenium-compat'
import cors from 'cors'
import helmet from 'helmet'
const app = ingenium()
app.use(expressCompat(cors({ origin: 'https://app.example.com' })))
app.use(expressCompat(helmet()))
Compatibility status
Validated end-to-end in packages/ingenium-compat/test/e2e.test.ts:
| Middleware | Status | Notes |
|---|---|---|
cors | supported | Simple requests + OPTIONS preflight. |
helmet | supported | All default security headers. |
cookie-parser | supported | req.cookies populated, mirrored to ctx.state.cookies. |
morgan | supported | End-of-request tokens (:status, :response-time) work - res emits finish. |
express-rate-limit | supported | req.ip is populated, so no custom keyGenerator is needed. |
compression | supported | The downstream response is replayed through the patched res, so the body is gzipped and Content-Encoding is set. |
body-parser | supported | Reads the real request stream; req.body mirrors to ctx.state.body. |
passport.initialize | supported | req._passport propagates to ctx.state. |
passport.authenticate | partial | res.redirect and cookie writes work; strategies needing a persisted session also need a session store. |
express-session | supported | Writes Set-Cookie via on-headers and saves on res.end. |
multer | supported | req.pipe(busboy) works against the real Readable; req.file mirrors to ctx.state. |
How it works
expressCompat(mw) returns a Ingenium middleware that, per request:
- Builds a
Readablereqand aWritableresover theIngeniumContext.- Headers and status proxy live to the context, so header-only middleware (
cors,helmet) land their changes with no body round-trip. - The request body is lazy - the underlying stream is only claimed when the middleware actually reads it, so header-only middleware pay nothing for it.
- Headers and status proxy live to the context, so header-only middleware (
- Runs the middleware:
- If it wrote the response (
res.json/send/end), the chain stops. - If it called
next(), the downstream chain runs. If the middleware patchedres.write/res.end(the compression / express-session pattern), the downstream response is replayed throughresso the patch takes effect; otherwise the fast path leaves the response untouched and emits a syntheticfinishfor observers likemorgan. - If it called
next(err)or threw, the wrapper rejects to the globalonErrorboundary.
- If it wrote the response (
- Mirrors
req.*mutations (req.user,req.body,req.cookies, ...) back intoctx.statebefore the downstream chain reads them.
Performance
The compat cost is opt-in and localized - paid only on requests that pass through a wrapped middleware. The core ingenium fast paths (O(k) trie routing, compile-time middleware composition, pooled context) are untouched, so native handlers run at full speed. Header-only middleware stay close to free thanks to the live header proxy and lazy body; body-transforming middleware land at roughly Express cost - only where you choose to use them.
Prefer the native primitives when starting fresh
The shim is for reusing existing Express middleware. For new code the Ingenium-native equivalents are integrated more tightly and skip the shim entirely:
- Body parsing ->
ctx.body.json()/ctx.body.multipart() - Sessions -> native
sessionMiddleware - CORS / CSRF / rate limiting -> the native middleware on
ingenium
Where to next?
- Migration from Express - the side-by-side diff.
- Body parsing - the native replacement for
body-parserandmulter. - Sessions - the native replacement for
express-session. - Production hardening - the full prod wiring.