GDPR Compliance
ChimerAI includes a ready-to-use GDPR toolkit covering data export, right-to-erasure, consent management, and a cookie banner — all wired to your database.
What you get
- Data export — Download all personal data as JSON (Art. 20)
- Right to erasure — Delete account + anonymise data (Art. 17)
- Consent management — Store and retrieve granular consent
- Cookie banner — GDPR-compliant opt-in/opt-out with Consent API
- Audit trail — Every GDPR action is logged for compliance
Quick setup
npx chimerai add gdpr
Scaffolds:
lib/gdpr.ts ← exportUserData, deleteUserData, recordConsent, getConsentStatus
app/gdpr/page.tsx ← self-service portal
app/api/gdpr/export/route.ts ← Art. 20 — data export (JSON download)
app/api/gdpr/delete/route.ts ← Art. 17 — right to erasure
app/api/gdpr/consent/route.ts ← Art. 7 — GET/POST consent record
components/CookieBanner.tsx ← cookie consent UI
Prisma schema changes
model ConsentLog {
id String @id @default(cuid())
userId String
type String // 'analytics' | 'marketing' | custom
granted Boolean
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@index([userId])
@@index([type])
}
After installation run:
npx prisma migrate dev --name add-consent-log
lib/gdpr.ts functions
import { exportUserData, deleteUserData, recordConsent, getConsentStatus } from '@/lib/gdpr';
// Art. 15 / Art. 20 — Export all personal data (strips password, mfaSecret)
const data = await exportUserData(session.user.id);
// Art. 17 — Right to erasure: cascade-deletes ConsentLog → Sessions → Accounts
// → APIKeys → Messages → Conversations → User
await deleteUserData(session.user.id);
// Art. 7 — Record a consent decision (creates immutable audit row with timestamp)
await recordConsent(userId, 'analytics', true);
await recordConsent(userId, 'marketing', false);
// Get latest decision per type
const status = await getConsentStatus(userId);
// { analytics: true, marketing: false }
Data export
// app/api/gdpr/export/route.ts
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
import { db } from '@/lib/db';
export async function GET() {
const session = await getServerSession(authOptions);
if (!session) return new Response('Unauthorized', { status: 401 });
const user = await db.user.findUnique({
where: { email: session.user!.email! },
include: { messages: true, conversations: true },
});
const exportData = {
exportedAt: new Date().toISOString(),
profile: { name: user!.name, email: user!.email },
conversations: user!.conversations,
messages: user!.messages,
};
return new Response(JSON.stringify(exportData, null, 2), {
headers: {
'Content-Type': 'application/json',
'Content-Disposition': `attachment; filename="my-data-${Date.now()}.json"`,
},
});
}
Right to erasure
// app/api/gdpr/delete/route.ts
import { db } from '@/lib/db';
import { getServerSession } from 'next-auth';
import { authOptions } from '@/lib/auth';
export async function DELETE() {
const session = await getServerSession(authOptions);
if (!session) return new Response('Unauthorized', { status: 401 });
// Anonymise instead of hard delete (preserves referential integrity)
await db.user.update({
where: { email: session.user!.email! },
data: {
name: 'Deleted User',
email: `deleted-${Date.now()}@anonymised.invalid`,
image: null,
deletedAt: new Date(),
},
});
return Response.json({ success: true });
}
Cookie banner consent
// app/api/gdpr/consent/route.ts
import { db } from '@/lib/db';
export async function POST(req: Request) {
const { userId, analytics, marketing } = await req.json();
await db.consent.upsert({
where: { userId },
update: { analytics, marketing, updatedAt: new Date() },
create: { userId, analytics, marketing },
});
return Response.json({ ok: true });
}
Further reading
- GDPR Guide — complete reference with all
lib/gdpr.tsfunction signatures and API route specs - GDPR Official Text
- ChimerAI Security Docs