Consenti

Backend — Return Value & Methods

createConsenti(config) returns an object with framework adapters, service references, and lifecycle controls. Nothing is a class — you get a plain object you can destructure or pass around.

Quick reference

PropertyTypeDescription
Framework adapters
router(req, res, next) => voidExpress / Connect middleware. Calls next() for unmatched routes.
handler(req, res) => Promise<void>Terminal Node.js HTTP handler. Does not call next. Use with raw http.createServer or as a Fastify handler.
fastifyHandlerFastify pluginDrop-in Fastify plugin. Register with fastify.register(consenti.fastifyHandler).
honoApp{ fetch: (req: Request) => Promise<Response> }Hono / WinterCG fetch handler. Works with Cloudflare Workers, Deno, Bun, and any runtime that supports the Fetch API.
Services
services.profileProfileServiceCreate, get, update, and delete consent profiles.
services.consentConsentServiceSubmit, update, erase, and verify consent records.
services.visitorVisitorServiceList and manage visitor records.
services.userUserServiceCreate and manage admin users programmatically.
Infrastructure
storageStorageAdapterRaw storage adapter. Use when the service layer does not expose what you need.
eventBusEventEmitterNode.js EventEmitter that fires lifecycle events. See Events.
readyPromise<void>Resolves after storage connects and the bootstrap admin user is created. Await this before accepting requests.
Lifecycle
destroy()() => Promise<void>Graceful shutdown — stops background jobs, runs plugin destroy() hooks. Call before process.exit().

router

Express / Connect compatible middleware. Handles all Consenti routes and calls next() for any path that does not match. Use this when Consenti shares a process with other Express routes.

Express
ts
import express from 'express'
import { createConsenti } from '@consenti/api'

const app = express()
const consenti = createConsenti({ /* ... */ })

app.use(consenti.router)  // Consenti handles /consenti/…; other routes fall through

app.get('/health', (req, res) => res.json({ ok: true }))

app.listen(3000)

handler

Terminal Node.js HTTP handler. Suitable for http.createServer, or as an all-routes Fastify handler. Does not call next() — if the request does not match, a JSON 404 is returned.

Standalone
ts
import { createServer } from 'node:http'
import { createConsenti } from '@consenti/api'

const consenti = createConsenti({ /* ... */ })
await consenti.ready

const server = createServer(consenti.handler)
server.listen(3000)

fastifyHandler

Pre-built Fastify plugin. It catches all paths and delegates to the Consenti handler, bypassing Fastify's routing layer for maximum compatibility.

Fastify
ts
import Fastify from 'fastify'
import { createConsenti } from '@consenti/api'

const fastify = Fastify()
const consenti = createConsenti({ /* ... */ })

await fastify.register(consenti.fastifyHandler)
await fastify.listen({ port: 3000 })

honoApp

WinterCG-compatible fetch handler. Conforms to the { fetch } interface expected by Hono, Cloudflare Workers, Deno, Bun, and Next.js Edge routes.

Hono
ts
import { Hono } from 'hono'
import { createConsenti } from '@consenti/api'

const consenti = createConsenti({ /* ... */ })

const app = new Hono()
app.all('/consenti/*', (c) => consenti.honoApp.fetch(c.req.raw))
Next.js App Router (catch-all)
ts
// app/consenti/[...path]/route.ts
import { createConsenti } from '@consenti/api'

const consenti = createConsenti({ /* ... */ })

const handler = (req: Request) => consenti.honoApp.fetch(req)

export { handler as GET, handler as POST, handler as PUT, handler as DELETE, handler as PATCH }

ready

A Promise<void> that resolves once the storage adapter has connected, migrations have run, and the bootstrap admin user has been created (first run only). Reject if storage fails to connect.

Always await ready before starting to accept requests in production — this ensures the admin user exists and the database schema is up to date.

ts
const consenti = createConsenti({ /* ... */ })

await consenti.ready  // ← guaranteed schema + admin user after this line

server.listen(3000, () => {
  console.log('Consenti ready at http://localhost:3000/consenti')
})
⚠️Skipping await ready is safe for fire-and-forget scripts, but in a server process it risks accepting requests before the database schema exists — causing crashes on the first query.

services

Direct programmatic access to the service layer — no HTTP overhead, no auth check. Use this for server-side scripts, scheduled jobs, or integration tests.

ts
const { services, storage } = createConsenti({ /* ... */ })

// Read a profile
const profile = await services.profile.get('profile-uuid')

// Submit consent programmatically (e.g. from a server-side form handler)
const record = await services.consent.create({
  visitorId: 'visitor-uuid',
  profileId: 'profile-uuid',
  profileVersion: 1,
  locale: 'en',
  consentJson: { analytics: 'granted', marketing: 'denied', necessary: 'granted' },
  gpcDetected: false,
  source: 'api',
})

// List recent visitors
const visitors = await services.visitor.list({ tenantId: 'default', page: 1, limit: 50 })

storage

The raw StorageAdapter instance. Use it when the service layer does not expose the operation you need, or when writing integration tests that seed data directly.

ts
const { storage } = createConsenti({ /* ... */ })
await consenti.ready

// Stream all consent records for an export job
for await (const record of storage.streamConsents({ tenantId: 'default' })) {
  await writeToWarehouse(record)
}

// Get overview stats directly
const stats = await storage.getOverviewStats('default')

destroy()

Runs graceful shutdown in order: stops the data-retention background timer, stops the GVL refresh job (if TCF is enabled), and calls destroy() on each registered plugin. Call this before process.exit()or in your framework's shutdown hook.

ts
// Express + graceful shutdown
process.on('SIGTERM', async () => {
  await consenti.destroy()
  server.close(() => process.exit(0))
})

// Fastify
fastify.addHook('onClose', async () => {
  await consenti.destroy()
})