Backend Frameworks
Each backend example builds the root container once, creates one request scope per HTTP request, exposes the scope through the framework-native request object, and disposes it from the response lifecycle.
They share examples/_shared/container.ts, so the differences below are the framework lifecycle hooks and adapter APIs.
| Example | Adapter |
|---|---|
fastify.ts | @inferdi/fastify |
hono.ts | @inferdi/hono |
koa.ts | @inferdi/koa |
express.ts | @inferdi/express |
elysia.ts | @inferdi/elysia |
Fastify
ts
import Fastify, { type FastifyInstance, type FastifyRequest } from 'fastify'
import { inferdiFastify } from '@inferdi/fastify'
import {
buildRootContainer,
createRequestScope,
type RootContainer,
type RequestContainer,
} from '../_shared/container.js'
const root = buildRootContainer()
declare module 'fastify' {
interface FastifyInstance {
di: RootContainer
}
interface FastifyRequest {
di: RequestContainer
}
}
function normalizeHeader(value: string | string[] | undefined): string | undefined {
return Array.isArray(value) ? value[0] : value
}
export function buildServer(): FastifyInstance {
const app = Fastify()
app.register(inferdiFastify, {
container: root,
// Annotate hook params: `app.register` cannot infer the plugin's generics.
createScope: (root: RootContainer, request: FastifyRequest) =>
createRequestScope(root, {
requestId: request.id,
ip: request.ip,
userId: normalizeHeader(request.headers['x-user-id']),
}),
})
app.get('/users/:id', async (request) => {
const { id } = request.params as { id: string }
return request.di.get('users').profile(id)
})
return app
}Repository file: examples/backend/fastify.ts
Hono
ts
import { Hono } from 'hono'
import { inferdiHono, type InferdiHonoEnv } from '@inferdi/hono'
import { buildRootContainer } from '../_shared/container.js'
const root = buildRootContainer()
type AppEnv = InferdiHonoEnv<typeof root>
export const app = new Hono<AppEnv>()
app.use('*', inferdiHono({
container: root,
setupScope: (scope, c) => {
const request = scope.get('request')
request.requestId = crypto.randomUUID()
request.userId = c.req.header('x-user-id')
},
}))
app.get('/users/:id', async (c) => {
const user = await c.var.di.get('users').profile(c.req.param('id'))
return c.json(user)
})Repository file: examples/backend/hono.ts
Koa
ts
import Koa from 'koa'
import { inferdiKoa } from '@inferdi/koa'
import {
buildRootContainer,
createRequestScope,
type RequestContainer,
} from '../_shared/container.js'
const root = buildRootContainer()
declare module 'koa' {
interface DefaultState {
di: RequestContainer
}
}
export const app = new Koa()
app.use(inferdiKoa({
container: root,
createScope: (root, ctx) =>
createRequestScope(root, {
requestId: crypto.randomUUID(),
ip: ctx.ip,
userId: ctx.get('x-user-id') || undefined,
}),
}))
app.use(async (ctx) => {
const id = ctx.path.split('/').pop() ?? ''
ctx.body = await ctx.state.di.get('users').profile(id)
})Repository file: examples/backend/koa.ts
Express
ts
import express from 'express'
import { inferdiExpress } from '@inferdi/express'
import {
buildRootContainer,
createRequestScope,
type RequestContainer,
} from '../_shared/container.js'
const root = buildRootContainer()
declare global {
namespace Express {
interface Request {
di: RequestContainer
}
}
}
function normalizeHeader(value: string | string[] | undefined): string | undefined {
return Array.isArray(value) ? value[0] : value
}
export const app = express()
app.use(inferdiExpress({
container: root,
createScope: (root, req) =>
createRequestScope(root, {
requestId: crypto.randomUUID(),
// req.ip is `string | undefined` in @types/express — propagate that shape.
ip: req.ip,
userId: normalizeHeader(req.headers['x-user-id']),
}),
}))
app.get('/users/:id', async (req, res, next) => {
try {
res.json(await req.di.get('users').profile(req.params.id))
} catch (error) {
next(error)
}
})Repository file: examples/backend/express.ts
Elysia
ts
import { Elysia } from 'elysia'
import { inferdiElysia } from '@inferdi/elysia'
import {
buildRootContainer,
createRequestScope,
} from '../_shared/container.js'
const root = buildRootContainer()
export const app = new Elysia()
.use(inferdiElysia({
container: root,
createScope: (root, { request }) =>
createRequestScope(root, {
requestId: crypto.randomUUID(),
userId: request.headers.get('x-user-id') ?? undefined,
}),
}))
.get('/users/:id', ({ params, di }) =>
di.get('users').profile(params.id),
)Repository file: examples/backend/elysia.ts
