Skip to content

Frameworks backend

Chaque exemple construit une seule fois la racine, crée un scope par requête HTTP et l’expose via l’objet de requête natif du framework. La libération suit le cycle de vie de la réponse.

Tous utilisent le même graphe. Les différences concernent les hooks de cycle de vie et les API des adaptateurs. examples/_shared/container.ts

ExempleAdaptateur
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'

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 root = buildRootContainer()
  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'])
      }),
    disposeRootOnClose: true
  })

  app.get('/users/:id', async (request) => {
    const { id } = request.params as { id: string }
    const users = await request.di.getAsync('users')
    return users.profile(id)
  })

  return app
}

Fichier du dépôt : examples/backend/fastify.ts

Hono

ts
import { Hono } from 'hono'
import { inferdiHono, type InferdiHonoScopeEnv } from '@inferdi/hono'

import {
  buildRootContainer,
  createRequestScope,
  type RequestContainer
} from '../_shared/container.js'

const root = buildRootContainer()
type AppEnv = InferdiHonoScopeEnv<RequestContainer>

export const app = new Hono<AppEnv>()

app.use('*', inferdiHono({
  container: root,
  createScope: (_root, c) => createRequestScope(root, {
    requestId: crypto.randomUUID(),
    userId: c.req.header('x-user-id')
  })
}))

app.get('/users/:id', async (c) => {
  const users = await c.var.di.getAsync('users')
  const user = await users.profile(c.req.param('id'))
  return c.json(user)
})

Fichier du dépôt : 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() ?? ''
  const users = await ctx.state.di.getAsync('users')
  ctx.body = await users.profile(id)
})

Fichier du dépôt : 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 {
    const users = await req.di.getAsync('users')
    res.json(await users.profile(req.params.id))
  } catch (error) {
    next(error)
  }
})

Fichier du dépôt : 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', async ({ params, di }) => {
    const users = await di.getAsync('users')
    return users.profile(params.id)
  })

Fichier du dépôt : examples/backend/elysia.ts