Skip to main content

Integrations

Where duck-iam plugs into the rest of your stack - adapters, server middleware, client SDKs, observability, and cross-instance invalidation.

What lives here

@gentleduck/iam's integration story is sliced by direction: where the data flows.

SubpathDirectionWhat it does
adapters/inboundWhere policies + roles + subjects come from (memory, file, redis, drizzle, prisma, http)
server/inbound + outboundExpress / Hono / Next / Nest middleware. Translates HTTP requests into authorize calls and authorize results back into HTTP responses
client/outboundReact / Vue / vanilla JS SDKs. Renders <Can> / <Cannot> / useAccess() from a snapshot the server pre-computed
invalidators/sidewaysCross-instance cache invalidation. One node mutates a policy; every other node's cache flips stale
observability/outboundOpenTelemetry metrics aggregator that turns onMetrics events into p50 / p95 / p99

Wiring all five at once

A production deployment that uses one of each:

import { IamEngine } from '@gentleduck/iam/core'
import { IamDrizzleAdapter } from '@gentleduck/iam/adapters/drizzle'
import { adminRouter, guard } from '@gentleduck/iam/server/express'
import { createIamRedisInvalidator } from '@gentleduck/iam/invalidators/redis'
import { iamCreateMetricsAggregator } from '@gentleduck/iam/observability/metrics'

const adapter = new IamDrizzleAdapter(db, schema)
const invalidator = createIamRedisInvalidator({ client: redis })
const metrics = iamCreateMetricsAggregator()

const engine = new IamEngine({
  adapter,
  invalidator,
  hooks: { onMetrics: metrics.record },
})

await engine.preload()  // warm caches before first request

app.delete('/posts/:id', guard(engine, 'delete', 'post'), handler)
app.use('/admin', adminRouter(engine, { authorize: (req) => isAdmin(req) })(() => express.Router()))

app.get('/healthz', async (_, res) => res.json(await engine.healthCheck()))
app.get('/metrics', (_, res) => res.json(metrics.snapshot()))

What's NOT here