Skip to main content
Back to Blog
GDPRComplianceWeb DevelopmentPrivacySecurity

GDPR-Compliant Web App Development: What You Need to Know

DEVOIDA Team
7 min read

GDPR compliance isn't optional—here's how to build it into your app from day one

GDPR Fundamentals for Developers

Key Principles

principlerequirementexample
LawfulnessValid legal basis for processingConsent, contract, legitimate interest
Purpose LimitationCollect for specific purposes onlyDon't use email for marketing without consent
Data MinimizationCollect only what you needDon't require phone number for newsletter
Storage LimitationDelete when no longer neededAuto-delete inactive accounts after 2 years
SecurityProtect data appropriatelyEncryption, 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

rightdescriptiondeadline
Right of AccessUser can request copy of their data30 days
Right to RectificationUser can correct inaccurate data30 days
Right to ErasureUser can request deletion30 days
Right to PortabilityUser can export data in machine-readable format30 days
Right to ObjectUser can opt out of processingImmediately

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