GDPR compliance isn't optional—here's how to build it into your app from day one
GDPR Fundamentals for Developers
Key Principles
| principle | requirement | example |
|---|---|---|
| Lawfulness | Valid legal basis for processing | Consent, contract, legitimate interest |
| Purpose Limitation | Collect for specific purposes only | Don't use email for marketing without consent |
| Data Minimization | Collect only what you need | Don't require phone number for newsletter |
| Storage Limitation | Delete when no longer needed | Auto-delete inactive accounts after 2 years |
| Security | Protect data appropriately | Encryption, access controls, auditing |
Consent Management Implementation
Cookie Consent Banner
// Consent management implementation
interface ConsentPreferences {
necessary: boolean; // Always true, required for site function
analytics: boolean; // Google Analytics, etc.
marketing: boolean; // Ad tracking, retargeting
preferences: boolean; // Language, theme preferences
timestamp: string;
version: string;
}
class ConsentManager {
private readonly CONSENT_KEY = 'gdpr_consent';
private readonly CONSENT_VERSION = '1.0';
getConsent(): ConsentPreferences | null {
const stored = localStorage.getItem(this.CONSENT_KEY);
if (!stored) return null;
const consent = JSON.parse(stored);
// Re-prompt if consent version changed
if (consent.version !== this.CONSENT_VERSION) return null;
return consent;
}
setConsent(preferences: Partial<ConsentPreferences>): void {
const consent: ConsentPreferences = {
necessary: true, // Always required
analytics: preferences.analytics || false,
marketing: preferences.marketing || false,
preferences: preferences.preferences || false,
timestamp: new Date().toISOString(),
version: this.CONSENT_VERSION,
};
localStorage.setItem(this.CONSENT_KEY, JSON.stringify(consent));
// Apply consent settings
this.applyConsent(consent);
// Log consent for records
this.logConsent(consent);
}
private applyConsent(consent: ConsentPreferences): void {
if (consent.analytics) {
this.enableAnalytics();
} else {
this.disableAnalytics();
}
if (consent.marketing) {
this.enableMarketing();
} else {
this.disableMarketing();
}
}
private enableAnalytics(): void {
// Initialize Google Analytics only after consent
window.gtag?.('consent', 'update', {
analytics_storage: 'granted'
});
}
private disableAnalytics(): void {
window.gtag?.('consent', 'update', {
analytics_storage: 'denied'
});
}
}
React Consent Banner Component
import { useState, useEffect } from 'react';
export function ConsentBanner() {
const [showBanner, setShowBanner] = useState(false);
const [preferences, setPreferences] = useState({
analytics: false,
marketing: false,
});
useEffect(() => {
const consent = consentManager.getConsent();
if (!consent) {
setShowBanner(true);
}
}, []);
const handleAcceptAll = () => {
consentManager.setConsent({
analytics: true,
marketing: true,
preferences: true,
});
setShowBanner(false);
};
const handleRejectAll = () => {
consentManager.setConsent({
analytics: false,
marketing: false,
preferences: false,
});
setShowBanner(false);
};
const handleSavePreferences = () => {
consentManager.setConsent(preferences);
setShowBanner(false);
};
if (!showBanner) return null;
return (
<div className="fixed bottom-0 left-0 right-0 bg-white shadow-lg p-6 z-50">
<h3 className="font-bold text-lg mb-2">Cookie Preferences</h3>
<p className="text-gray-600 mb-4">
We use cookies to enhance your experience. Choose your preferences below.
</p>
<div className="space-y-2 mb-4">
<label className="flex items-center">
<input type="checkbox" checked disabled className="mr-2" />
<span>Necessary (Required)</span>
</label>
<label className="flex items-center">
<input
type="checkbox"
checked={preferences.analytics}
onChange={(e) => setPreferences(p => ({...p, analytics: e.target.checked}))}
className="mr-2"
/>
<span>Analytics</span>
</label>
<label className="flex items-center">
<input
type="checkbox"
checked={preferences.marketing}
onChange={(e) => setPreferences(p => ({...p, marketing: e.target.checked}))}
className="mr-2"
/>
<span>Marketing</span>
</label>
</div>
<div className="flex gap-2">
<button onClick={handleRejectAll} className="px-4 py-2 border rounded">
Reject All
</button>
<button onClick={handleSavePreferences} className="px-4 py-2 border rounded">
Save Preferences
</button>
<button onClick={handleAcceptAll} className="px-4 py-2 bg-blue-600 text-white rounded">
Accept All
</button>
</div>
</div>
);
}
User Rights Implementation
Data Subject Rights
| right | description | deadline |
|---|---|---|
| Right of Access | User can request copy of their data | 30 days |
| Right to Rectification | User can correct inaccurate data | 30 days |
| Right to Erasure | User can request deletion | 30 days |
| Right to Portability | User can export data in machine-readable format | 30 days |
| Right to Object | User can opt out of processing | Immediately |
Data Export API
// API endpoint for data export (Right to Portability)
app.get('/api/user/export', authenticate, async (req, res) => {
const userId = req.user.id;
try {
// Collect all user data
const userData = {
profile: await getUserProfile(userId),
orders: await getUserOrders(userId),
preferences: await getUserPreferences(userId),
activityLog: await getUserActivity(userId),
exportDate: new Date().toISOString(),
format: 'JSON (GDPR Article 20 compliant)'
};
// Log the export request
await logDataRequest({
userId,
type: 'EXPORT',
timestamp: new Date(),
ipAddress: req.ip
});
res.setHeader('Content-Type', 'application/json');
res.setHeader('Content-Disposition', `attachment; filename="user-data-${userId}.json"`);
res.json(userData);
} catch (error) {
console.error('Data export failed:', error);
res.status(500).json({ error: 'Export failed' });
}
});
Account Deletion API
// API endpoint for account deletion (Right to Erasure)
app.delete('/api/user/account', authenticate, async (req, res) => {
const userId = req.user.id;
try {
// Soft delete first (for 30-day recovery period)
await db.users.update({
where: { id: userId },
data: {
status: 'PENDING_DELETION',
deletionRequestedAt: new Date(),
deletionScheduledFor: addDays(new Date(), 30)
}
});
// Schedule hard delete
await queue.add('hardDeleteUser', { userId }, {
delay: 30 * 24 * 60 * 60 * 1000 // 30 days
});
// Anonymize activity logs (keep for analytics but remove PII)
await anonymizeUserLogs(userId);
// Revoke all sessions
await revokeUserSessions(userId);
// Send confirmation email
await sendEmail({
to: req.user.email,
template: 'account-deletion-confirmed',
data: { deletionDate: addDays(new Date(), 30) }
});
res.json({
message: 'Account scheduled for deletion',
deletionDate: addDays(new Date(), 30)
});
} catch (error) {
console.error('Account deletion failed:', error);
res.status(500).json({ error: 'Deletion failed' });
}
});
Data Security Requirements
Encryption Implementation
import crypto from 'crypto';
class DataEncryption {
private readonly algorithm = 'aes-256-gcm';
private readonly key: Buffer;
constructor(encryptionKey: string) {
this.key = Buffer.from(encryptionKey, 'hex');
}
encrypt(plaintext: string): { encrypted: string; iv: string; tag: string } {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(this.algorithm, this.key, iv);
let encrypted = cipher.update(plaintext, 'utf8', 'hex');
encrypted += cipher.final('hex');
return {
encrypted,
iv: iv.toString('hex'),
tag: cipher.getAuthTag().toString('hex')
};
}
decrypt(encrypted: string, iv: string, tag: string): string {
const decipher = crypto.createDecipheriv(
this.algorithm,
this.key,
Buffer.from(iv, 'hex')
);
decipher.setAuthTag(Buffer.from(tag, 'hex'));
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
decrypted += decipher.final('utf8');
return decrypted;
}
}
// Usage for sensitive fields
const encryption = new DataEncryption(process.env.ENCRYPTION_KEY);
// Encrypt before storing
const encryptedSSN = encryption.encrypt(user.ssn);
await db.users.update({
where: { id: userId },
data: {
ssnEncrypted: encryptedSSN.encrypted,
ssnIv: encryptedSSN.iv,
ssnTag: encryptedSSN.tag
}
});
GDPR Compliance Checklist
## Technical Requirements
### Data Collection
- [ ] Consent banner implemented
- [ ] Granular consent options (analytics, marketing, etc.)
- [ ] Consent logged with timestamp
- [ ] Double opt-in for email marketing
- [ ] Privacy policy linked and accessible
### Data Storage
- [ ] Personal data encrypted at rest
- [ ] Encryption in transit (HTTPS)
- [ ] Access controls implemented
- [ ] Data retention policies defined
- [ ] Regular data purging automated
### User Rights
- [ ] Data export functionality
- [ ] Account deletion workflow
- [ ] Profile update capabilities
- [ ] Consent withdrawal option
- [ ] Data processing objection mechanism
### Security
- [ ] Access logging enabled
- [ ] Breach notification process
- [ ] Regular security audits
- [ ] Employee access limited (need-to-know)
- [ ] Third-party processors vetted (DPA signed)
### Documentation
- [ ] Privacy policy up to date
- [ ] Records of processing activities
- [ ] Data flow documentation
- [ ] Breach response plan
- [ ] DPO contact (if required)
Need help with GDPR-compliant development?
We build privacy-first applications with GDPR compliance built in from day one.
Get Compliance Guidance