GDPR Art. 15 grants every EU data subject the right to receive a copy of all personal data held about them, within 30 days of requesting it. An application with no data export feature and no DSAR process is not compliant — not having a formal process is itself a violation, regardless of whether any user has actually made a request. When a subject access request does arrive (and they will, especially as GDPR awareness grows), scrambling to manually assemble a data export from multiple tables is both error-prone and legally risky. Incomplete exports — for example, returning profile fields but omitting activity history or consent records — also violate Art. 15.
High because the absence of any DSAR mechanism makes it structurally impossible to comply with Art. 15's 30-day fulfillment deadline, leaving the controller in ongoing breach for every day the gap exists.
Implement a self-service data export endpoint in app/api/user/export/route.ts that gathers all personal data held about the authenticated user.
export async function GET() {
const session = await getServerSession()
if (!session?.user?.id) return new Response('Unauthorized', { status: 401 })
const userId = session.user.id
const [profile, activity, purchases, consents] = await Promise.all([
db.user.findUnique({ where: { id: userId }, select: { id: true, email: true, name: true, createdAt: true } }),
db.activityLog.findMany({ where: { userId } }),
db.order.findMany({ where: { userId }, include: { items: true } }),
db.consentRecord.findMany({ where: { userId } }),
])
return new Response(JSON.stringify({ _meta: { exportedAt: new Date().toISOString() }, profile, activity, purchases, consents }, null, 2), {
headers: {
'Content-Type': 'application/json',
'Content-Disposition': `attachment; filename="my-data-${Date.now()}.json"`,
},
})
}
Add a "Download my data" button in account settings. Log each export request with a timestamp to demonstrate compliance with the 30-day fulfillment requirement per Art. 12(3).
ID: gdpr-readiness.user-rights.right-to-access
Severity: high
What to look for: Look for a user settings or profile page with a "Download My Data," "Request My Data," or "Data Subject Access Request" option. Check API routes for a data export or DSAR endpoint (common paths: /api/user/export, /api/account/download, /api/dsar, /api/me/data). Inspect what the export includes: does it cover all major data types held about the user — profile fields, activity history, uploaded content, messages, purchase history, consent records? Is the response delivered immediately, or is there an email-based fulfillment workflow? GDPR requires DSARs to be fulfilled within 30 days. If using an email-based workflow, check whether there is a documented process for handling and tracking requests within the deadline. Count every data store containing personal data (primary database, analytics, logs, third-party services) and enumerate which are included in the SAR response vs. which are omitted.
Pass criteria: Authenticated users can initiate a data access request from a settings or profile page, or by contacting a documented privacy contact. The resulting export covers all personal data held about the user. If automated, it is delivered immediately or within a documented timeframe. If manual, the process is documented and tracks the 30-day deadline. Report even on pass: "SAR response covers X of Y identified data stores. Export format includes all required personal data categories." Threshold: no more than 30 days to fulfill a subject access request.
Fail criteria: No data access feature or DSAR process exists. Export exists but is incomplete (e.g., only profile info, missing activity history or purchases). No documented process for handling email-based requests.
Skip (N/A) when: Application collects no personal data beyond authentication credentials and has no content, history, or profile data to export.
Cross-reference: The right-to-portability check verifies the machine-readable export format that complements this human-readable access right.
Detail on fail: Example: "No data export feature and no documented DSAR process. Users cannot request a copy of their data." or "Export endpoint exists but only returns profile fields; activity history and consent records are excluded." or "Privacy policy says to email privacy@example.com but no internal DSAR tracking process documented.".
Remediation: Implement a self-service data export endpoint:
// app/api/user/export/route.ts
import { getServerSession } from 'next-auth'
import { db } from '@/lib/db'
export async function GET() {
const session = await getServerSession()
if (!session?.user?.id) return new Response('Unauthorized', { status: 401 })
const userId = session.user.id
// Gather ALL personal data held about this user
const [profile, activity, purchases, consents, preferences] = await Promise.all([
db.user.findUnique({
where: { id: userId },
select: { id: true, email: true, name: true, createdAt: true, updatedAt: true }
}),
db.activityLog.findMany({ where: { userId }, orderBy: { createdAt: 'desc' } }),
db.order.findMany({ where: { userId }, include: { items: true } }),
db.consentRecord.findMany({ where: { userId }, orderBy: { recordedAt: 'desc' } }),
db.userPreference.findUnique({ where: { userId } }),
])
const exportData = {
_meta: {
exportedAt: new Date().toISOString(),
formatVersion: '1.0',
description: 'Personal data export — all data held about you.',
},
profile,
activityHistory: activity,
purchases,
consentHistory: consents,
preferences,
}
return new Response(JSON.stringify(exportData, null, 2), {
headers: {
'Content-Type': 'application/json',
'Content-Disposition': `attachment; filename="my-data-${Date.now()}.json"`,
},
})
}
Add a "Download my data" button in the account settings page. Log each DSAR request with a timestamp to demonstrate compliance with the 30-day fulfillment requirement.