GDPR Article 17 Erasure Implementation Code: A Developer Guide
The European Union’s General Data Protection Regulation grants individuals the right to request deletion of their personal data. Article 17, formally titled “Right to Erasure,” creates specific obligations for organizations handling EU residents’ data. For developers building compliance-minded applications, implementing these requirements requires careful architectural planning and robust code patterns.
This guide provides concrete implementation strategies for handling GDPR Article 17 erasure requests programmatically.
Understanding Article 17 Requirements
Article 17 establishes that data subjects can request erasure when:
- The data is no longer necessary for the original collection purpose
- They withdraw consent (where consent was the legal basis)
- They object to processing
- The data was unlawfully processed
- Erasure is required for legal compliance
Your system must handle these requests within one month, with possible extensions for complex cases.
Database-Level Erasure Implementation
The most critical component is ensuring complete data removal. A common mistake is only deleting from the primary table while leaving related data intact.
import asyncio
from sqlalchemy import delete, select
from sqlalchemy.ext.asyncio import AsyncSession
class ErasureService:
def __init__(self, session: AsyncSession):
self.session = session
async def process_erasure_request(self, user_id: str):
# Delete from all related tables in proper order
await self._delete_user_sessions(user_id)
await self._delete_user_logs(user_id)
await self._delete_user_profile(user_id)
await self._delete_user_account(user_id)
await self.session.commit()
async def _delete_user_sessions(self, user_id: str):
stmt = delete(Session).where(Session.user_id == user_id)
await self.session.execute(stmt)
async def _delete_user_logs(self, user_id: str):
stmt = delete(UserActivityLog).where(UserActivityLog.user_id == user_id)
await self.session.execute(stmt)
async def _delete_user_profile(self, user_id: str):
stmt = delete(UserProfile).where(UserProfile.user_id == user_id)
await self.session.execute(stmt)
async def _delete_user_account(self, user_id: str):
stmt = delete(User).where(User.id == user_id)
await self.session.execute(stmt)
API Endpoint for Erasure Requests
Your application needs a dedicated endpoint to receive erasure requests. This endpoint should authenticate the requester and initiate the erasure workflow.
// Express.js erasure request handler
app.delete('/api/v1/data-erasure',
requireAuth,
async (req, res) => {
const userId = req.user.id;
// Log the request for audit trail
await AuditLogger.log({
action: 'ERASURE_REQUEST',
userId,
timestamp: new Date(),
ipAddress: req.ip
});
// Queue erasure job
await erasureQueue.add('process-erasure', {
userId,
requestId: generateRequestId(),
requestedAt: new Date()
});
res.status(202).json({
message: 'Erasure request received',
requestId: generateRequestId(),
estimatedCompletion: '30 days'
});
}
);
Handling Third-Party Data Sharing
Article 17 requires notifying third parties about erasure requests when you’ve shared the data with them. Implement a tracking system for all data recipients.
from dataclasses import dataclass
from datetime import datetime
@dataclass
class DataRecipient:
name: str
endpoint: str
data_categories: list[str]
notified: bool = False
class ThirdPartyNotifier:
def __init__(self, recipients: list[DataRecipient]):
self.recipients = recipients
async def notify_all_recipients(self, user_id: str, request_id: str):
results = []
for recipient in self.recipients:
try:
response = await self._send_erasure_notification(
recipient.endpoint,
{
'subject_id': user_id,
'request_id': request_id,
'action_required': 'erase_all_personal_data'
}
)
results.append({
'recipient': recipient.name,
'status': 'notified',
'response': response
})
except Exception as e:
results.append({
'recipient': recipient.name,
'status': 'failed',
'error': str(e)
})
return results
async def _send_erasure_notification(self, endpoint: str, payload: dict):
async with aiohttp.ClientSession() as session:
async with session.post(endpoint, json=payload) as response:
response.raise_for_status()
return await response.json()
Soft Delete vs. Hard Delete Considerations
You must decide between soft deletion (marking records as deleted) and hard deletion (permanent removal). Consider these factors:
Soft delete approach:
-- Keep data but mark as deleted
UPDATE users
SET deleted_at = NOW(),
email = CONCAT(id, '_erased_', UNIX_TIMESTAMP(), '@erased.local'),
is_anonymized = true
WHERE id = ?;
Hard delete approach:
-- Permanent removal
DELETE FROM users WHERE id = ?;
DELETE FROM user_sessions WHERE user_id = ?;
DELETE FROM user_logs WHERE user_id = ?;
GDPR permits retaining data for legal obligations even after erasure requests. Implement a system to handle data that must be kept versus data that can be fully deleted.
Audit Trail Requirements
Maintain documentation of erasure actions. This proves compliance if challenged.
import logging
from datetime import datetime
class ErasureAuditLogger:
def __init__(self):
self.logger = logging.getLogger('gdpr.erasure')
def log_erasure_event(self, event_type: str, user_id: str, details: dict):
self.logger.info(
f"ERASURE_EVENT | {datetime.utcnow().isoformat()} | "
f"{event_type} | user_id={user_id} | details={details}"
)
def generate_compliance_report(self, user_id: str) -> dict:
# Return formatted report for data subject
return {
'request_date': self.get_request_date(user_id),
'completion_date': datetime.utcnow().isoformat(),
'data_categories_erased': self.get_erased_categories(user_id),
'third_parties_notified': self.get_notified_parties(user_id),
'retention_justifications': self.get_retained_data(user_id)
}
Practical Implementation Checklist
When building your erasure system, ensure you address:
- Link all data to a single user identifier across systems.
- Run large-scale deletion as background jobs.
- Confirm user identity before processing any erasure.
- Document legal bases for any data retained after an erasure request.
- Notify users when erasure completes.
- Expose an API endpoint to enable integration with external privacy tools.
These patterns cover the core obligations: complete database deletion, third-party notification, and an audit trail for regulatory review. Adapt them to your stack, and maintain documentation sufficient to demonstrate compliance if challenged.
Built by theluckystrike — More at zovo.one