Backend — Events
createConsenti() returns an eventBus — a standard Node.js EventEmitter — that emits typed events at every data lifecycle step. Subscribe to hook into consent saves, profile changes, and the server ready signal without polling the database or modifying routes.
import { createConsenti } from '@consenti/api'
const { eventBus, ready } = createConsenti({ /* ... */ })
eventBus.on('consent.created', (record) => {
console.log('New consent from visitor', record.visitorId)
})
// Wait for storage + bootstrap to complete before accepting requests
await readyEvent reference
| Event | Emitted when | Payload |
|---|---|---|
ready | Storage connected and admin user bootstrapped | none |
consent.created | A new consent record is saved for the first time | ConsentDbRecord |
consent.updated | An existing consent record is updated | { previous: ConsentDbRecord, current: ConsentDbRecord } |
consent.erased | A visitor's data is erased (GDPR right to erasure) | { visitorId: string } |
visitor.created | A new visitor record is created | Visitor |
profile.created | A consent profile is created | Profile |
profile.updated | A consent profile is updated (version bumped) | { previous: Profile, current: Profile } |
profile.deleted | A consent profile is deleted | { id: string, previous: Profile } |
cache:warm | Locale JSON files are written to disk (profile create or activate) | { paths: string[], version: number } |
cache:purge | Locale JSON files are removed (profile update, deactivate, or delete) | { paths: string[], version: number } |
ready
Emitted once after the storage adapter connects, migrations run, and the bootstrap admin user is created (if needed). This maps to the ready Promise returned by createConsenti() — they resolve at the same time.
Use the Promise form when you need to await startup before accepting requests. Use the event form when you want a fire-and-forget side effect.
// Promise form (preferred for startup sequencing)
const consenti = createConsenti({ /* ... */ })
await consenti.ready
server.listen(3000)
// Event form (fire-and-forget side effects)
consenti.eventBus.on('ready', () => {
console.log('[consenti] backend ready')
})consent.created
Fires after a new ConsentDbRecord is persisted — after plugins' afterConsentSave hooks run. Useful for webhooks, audit side-cars, or real-time analytics.
import type { ConsentDbRecord } from '@consenti/api'
eventBus.on('consent.created', async (record: ConsentDbRecord) => {
await fetch('https://webhook.example.com/consent-created', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
visitorId: record.visitorId,
profileId: record.profileId,
consent: record.consentJson,
gpc: record.gpcDetected,
timestamp: record.createdAt,
}),
})
})consent.updated
Fires when a visitor submits new consent choices that differ from their existing record. The payload includes both the previous and current state so you can compute a diff.
import type { ConsentDbRecord } from '@consenti/api'
eventBus.on('consent.updated', ({ previous, current }: {
previous: ConsentDbRecord
current: ConsentDbRecord
}) => {
const prev = previous.consentJson
const next = current.consentJson
for (const cookieId of Object.keys(next)) {
if (prev[cookieId] !== next[cookieId]) {
console.log(`${cookieId}: ${prev[cookieId]} → ${next[cookieId]}`)
}
}
})consent.erased
Fires after a visitor exercises their GDPR right to erasure. The visitor's consent record and visitor row are deleted from storage before this event fires.
eventBus.on('consent.erased', ({ visitorId }: { visitorId: string }) => {
// Mirror the deletion to a data warehouse or external DMP
myDmpClient.deleteVisitor(visitorId)
})visitor.created
Fires when Consenti creates a new visitor row — typically on first consent submission from that visitor. IPs are never available here; the record stores only the SHA-256 hash (ipHash).
import type { Visitor } from '@consenti/api'
eventBus.on('visitor.created', (visitor: Visitor) => {
console.log('New visitor in region:', visitor.region ?? 'unknown')
})profile.created / profile.updated / profile.deleted
Profile lifecycle events. profile.updated fires whenever a profile version is bumped — useful for cache invalidation in edge deployments. The previous profile is included so you can compare changes.
import type { Profile } from '@consenti/api'
eventBus.on('profile.created', (profile: Profile) => {
console.log('New profile:', profile.id, 'v' + profile.version)
})
eventBus.on('profile.updated', ({ previous, current }: {
previous: Profile
current: Profile
}) => {
console.log('Profile', current.id, 'bumped from v' +
previous.version + ' to v' + current.version)
})
eventBus.on('profile.deleted', ({ id, previous }: { id: string; previous: Profile }) => {
console.log('Profile deleted:', id, '(was v' + previous.version + ')')
})cache:warm / cache:purge
Fired after Consenti writes or removes locale JSON files on disk. Use these to integrate with a CDN, nginx cache, or any edge layer that needs to be invalidated or pre-warmed when a profile changes.
cache:warm fires when new files are written (profile created or activated). cache:purge fires when files are removed (profile updated, deactivated, or deleted). Both mirror the handleCache config callback — use whichever suits your integration.
eventBus.on('cache:warm', ({ paths, version }: { paths: string[]; version: number }) => {
// paths — array of locale file paths written to disk, e.g.
// ['./consenti-data/profiles/default/opt-in/en.json', '...fr-FR.json']
for (const p of paths) {
cdnClient.warmPath(p)
}
})
eventBus.on('cache:purge', ({ paths, version }: { paths: string[]; version: number }) => {
for (const p of paths) {
cdnClient.purge(p)
}
})handleCache config option is equivalent to listening on both cache:warm and cache:purge:isPurge: false → cache:warm, isPurge: true → cache:purge. Use whichever style fits your integration..catch() to avoid crashing the process.