Elysia Adapter
@inferdi/elysia is an Elysia v1 plugin. In scoped mode it creates one request scope, exposes it on Elysia context, keeps it available for user error handlers, and disposes it from onAfterResponse.
Install
pnpm add @inferdi/inferdi @inferdi/elysia elysiaimport { Elysia } from 'elysia'
import { inferdiElysia } from '@inferdi/elysia'Request Scope
const root = buildRootContainer()
const app = new Elysia()
.use(inferdiElysia({
container: root,
setupScope: (scope, { request }) => {
const ctx = scope.get('request')
ctx.requestId = crypto.randomUUID()
ctx.userId = request.headers.get('x-user-id') ?? undefined
},
}))
.get('/users/:id', ({ di, params }) =>
di.get('users').profile(params.id),
)For a custom context key:
const app = new Elysia()
.use(inferdiElysia({ container: root, key: 'container' }))
.get('/users/:id', ({ container, params }) =>
container.get('users').profile(params.id),
)Routes must be registered after .use(inferdiElysia(...)) in the typed Elysia chain.
Options
| Option | Default | Description |
|---|---|---|
container | required | Root container. |
key | 'di' | Elysia context key. |
scopePerRequest | true | Set false for root-only mode. |
createScope | root.createScope() | Custom request scope creation. |
setupScope | none | Hydrates before validation and route handlers. |
setupValidatedScope | none | Hydrates after Elysia validation. |
disposeScope | scope.dispose() | Custom disposal. |
autoDispose | true | false or predicate false transfers ownership. |
onDisposeError | console.error | Cleanup failure sink. |
Root-Only Mode
const app = new Elysia()
.use(inferdiElysia({
container: root,
scopePerRequest: false,
}))
.get('/health', ({ di }) => di.get('health').check())Root-only mode exposes the root container and installs no request-scope lifecycle hooks. Scoped-only options are statically rejected.
Lifecycle Notes
Cleanup is lifecycle-bound to onAfterResponse. If Elysia never reaches that hook, the adapter cannot release resources held by the request scope. The per-request bookkeeping is weakly held, but resource disposal requires the lifecycle hook to run.
Use setupScope for values needed before validation. Use setupValidatedScope for values derived from validated body, query, params, headers, or cookies.
Streaming
Elysia can produce a streaming Response before the stream drains. If scoped services are used after the route returns, call skipInferdiDispose(context) and dispose the scope yourself.
import { skipInferdiDispose } from '@inferdi/elysia'
app.get('/events', (context) => {
skipInferdiDispose(context)
const scope = context.di
const events = scope.get('events')
return new Response(new ReadableStream({
async start(controller) {
const encoder = new TextEncoder()
try {
for await (const event of events.subscribe()) {
controller.enqueue(encoder.encode(`data: ${JSON.stringify(event)}\n\n`))
}
} finally {
await scope.dispose()
}
},
}))
})skipInferdiDispose suppresses only successful cleanup. Error paths still dispose.
