GDPR Compliance
Rules
- Implement a data deletion endpoint (right to erasure): delete or anonymize all user PII on request
- Track explicit consent with timestamps: what the user consented to, when, and which version of the policy
- Anonymize PII in analytics and logs — use opaque IDs, never raw emails or names
- Implement a data export endpoint (right to portability): return all user data as JSON or CSV
- Show a cookie consent banner: no tracking scripts loaded until the user opts in
- Link to a privacy policy from every page with data collection forms
- Maintain an audit log for PII access — who viewed what personal data and when
- 72-hour breach notification: have a documented process and contact list ready
app.delete("/api/user/data", requireAuth, async (req, res) => {
const userId = req.user.id;
await db.transaction(async (tx) => {
await tx.delete(userProfiles).where(eq(userProfiles.userId, userId));
await tx.delete(userSessions).where(eq(userSessions.userId, userId));
await tx.update(users).set({
email: `deleted_${userId}@anonymized.local`,
name: "Deleted User",
deletedAt: new Date(),
}).where(eq(users.id, userId));
});
await auditLog("user_data_deleted", { userId, requestedBy: userId });
res.json({ success: true });
});
const consentSchema = z.object({
marketing: z.boolean(),
analytics: z.boolean(),
policyVersion: z.string(),
});
app.post("/api/consent", requireAuth, async (req, res) => {
const consent = consentSchema.parse(req.body);
await db.insert(consentRecords).values({
userId: req.user.id,
...consent,
consentedAt: new Date(),
ipAddress: req.ip,
});
res.json({ success: true });
});
Avoid
- Soft-deleting user data without actually removing PII — "deleted" users' emails still in the database
- Loading Google Analytics or tracking pixels before cookie consent
- Storing raw emails in analytics events or application logs
- No data export mechanism — users have a legal right to their data
- Assuming GDPR only applies to EU-based companies — it applies to any app serving EU users