Backend Integration

Deploy the tenant-scoped, fail-closed Next.js, Express, Prisma, and Drizzle source recipes from @tantainnovative/ndpr-recipes.

Use the recipes as owned source

@tantainnovative/ndpr-recipes is a versioned source package, not a hosted persistence service. Pin a version, copy the schema, request-context seam, routes, and adapters that you need, then connect them to your authentication and deployment conventions.

pnpm add @tantainnovative/ndpr-toolkit
pnpm add -D @tantainnovative/ndpr-recipes

# Prisma schema
cp node_modules/@tantainnovative/ndpr-recipes/prisma/schema.prisma prisma/schema.prisma

# Copy source before adapting imports and auth integration
cp -r node_modules/@tantainnovative/ndpr-recipes/src src/ndpr-recipes

Keep copied recipe files in your own source control. Future package updates should be reviewed and merged like application code rather than copied over local authentication, retention, or audit changes.

Connect the security context first

Every maintained route resolves identity through request-context.ts before accessing Prisma. The default actor resolver returns no actor, so staff routes fail closed until you connect a verified server session.

# Server-only configuration
DATABASE_URL="postgresql://user:password@localhost:5432/myapp_dev"
NDPR_TENANT_ID="tenant-production-id"
  • NDPR_TENANT_ID is the only tenant authority used by the recipes.
  • Map verified application roles to ndpr:staff or ndpr:admin.
  • Resolve actor ID, profile fields, roles, and account subject ID from a verified server session, never a body, query string, cookie, or arbitrary identity header.
  • Validate and normalize authoritative profile fields, especially staff email, in your authentication integration; server derivation prevents client impersonation but does not certify arbitrary profile strings.
  • Consent and subject DSR operations require a verified account subject or an anon_<UUID> capability in X-NDPR-Subject-Id.
  • DPIA, breach, ROPA, compliance, registration, and DSR administration require a verified staff actor.
// Replace the fail-closed default in request-context.ts.
export async function resolveVerifiedNDPRActor(request: NextRequest) {
  const session = await resolveYourVerifiedSession(request);
  if (!session?.user) return null;

  return {
    id: session.user.id,
    displayName: session.user.name,
    email: session.user.email,
    subjectId: session.user.dataSubjectId,
    roles: session.user.canManagePrivacy ? ['ndpr:staff'] : [],
  };
}

Do not restore the old ?subjectId=... pattern. Request bodies and query parameters cannot choose a tenant, subject, actor, role, reporter, assessor, or audit principal.

Install the canonical database schema

Copy the schema file instead of recreating it from a shortened documentation model. The maintained Prisma and Drizzle schemas include tenant keys and indexes, active-consent uniqueness, soft-removal fields, lossless JSON snapshots, and tenant/actor-scoped audit indexes.

# Fresh databases only — review before applying
# Prisma
cp node_modules/@tantainnovative/ndpr-recipes/prisma/schema.prisma prisma/schema.prisma
pnpm exec prisma migrate dev --name add-ndpr-compliance
pnpm exec prisma generate

# Drizzle
cp node_modules/@tantainnovative/ndpr-recipes/src/drizzle/schema.ts src/db/ndpr-schema.ts
pnpm exec drizzle-kit generate
pnpm exec drizzle-kit migrate

Those generation commands are for a fresh database. For populated 0.1.x or 0.2.0 recipe tables, do not use a blind db push or an auto-generated replacement migration. Use the packaged, reviewed migrations/0.3.0/postgresql.sql for standalone psql/Prisma, or the transaction-control-free migrations/0.3.0/drizzle.sqlinside Drizzle's managed transaction so schema changes and its ledger insert remain atomic. Follow the mapping, rehearsal, evidence-backfill, verification, and rollback steps in the Production Recipes guide.

Review every fresh-install or upgrade migration before applying it. Preserve compound tenant keys and tenant predicates if you merge the models into an existing schema.

Construct database adapters on the server

Prisma and Drizzle adapters require explicit trusted context objects. They are server-only persistence code; do not import a database client into a React component. A browser hook or preset should call your route throughapiAdapter instead.

Prisma contexts

import {
  prismaBreachAdapter,
  prismaConsentAdapter,
  prismaDSRAdapter,
  prismaROPAAdapter,
} from '@/ndpr-recipes/adapters';

// Resolve this context and enforce its requirement before construction.
const subject = {
  tenantId: context.tenantId,
  subjectId: context.subjectId!,
};

const consent = prismaConsentAdapter(prisma, {
  ...subject,
  ipAddress: request.headers.get('x-forwarded-for') ?? undefined,
  userAgent: request.headers.get('user-agent') ?? undefined,
});
const dsr = prismaDSRAdapter(prisma, subject);

// Staff-only workflows still receive tenant scope explicitly.
const breach = prismaBreachAdapter(prisma, { tenantId: context.tenantId });
const ropa = prismaROPAAdapter(
  prisma,
  { tenantId: context.tenantId },
  {
    organizationName: process.env.ORG_NAME!,
    organizationContact: process.env.DPO_EMAIL!,
    organizationAddress: process.env.ORG_ADDRESS!,
  },
);

Drizzle contexts

const subject = {
  tenantId: context.tenantId,
  subjectId: context.subjectId!,
};

const consent = drizzleConsentAdapter(db, {
  ...subject,
  ipAddress: request.ip,
  userAgent: request.get('user-agent'),
});
const dsr = drizzleDSRAdapter(db, subject);

The adapters advertise server-acknowledged storage capabilities, but your application remains responsible for database availability, backups, retention, encryption, concurrency policy, and evidentiary controls.

Next.js App Router recipes

Copy the API routes together with request-context.ts, shared-contracts.ts, and operational-indicators.ts. Adapt import paths after placing them in your application.

RouteMethodsRequirementBehavior
ConsentGET, POST, DELETEVerified subjectTenant/subject-scoped snapshots and revocation
DSRGET, POST, PATCHSubject intake; staff administrationTenant/subject intake and staff workflow
DPIAGET, POST, PUT, DELETENDPR staffLossless assessment snapshots and derived risk evidence
BreachGET, POST, PATCHNDPR staffIncident evidence and NDPC readiness metadata
ROPAGET, POST, PATCH, DELETENDPR staffComplete processing records and lossless snapshots
ComplianceGETNDPR staffServer-calculated implementation-readiness indicators; not legal certification
RegistrationGETNDPR staffDCPMI classification and CAR schedule

Every lookup and mutation includes the server tenant. Consent is additionally subject-scoped. Mutating routes write the business record and accountability event in one transaction; breach reporter details, DPIA assessor and conductor details, risk evidence, and audit principals are derived on the server.

Anonymous consent client

import { apiAdapter } from '@tantainnovative/ndpr-toolkit/adapters';

// Generate once and retain it in first-party storage. It is a scoped capability,
// not an authenticated account or a staff credential.
const subjectId = `anon_${crypto.randomUUID()}`;

const consentAdapter = apiAdapter('/api/consent', {
  credentials: 'same-origin',
  headers: { 'X-NDPR-Subject-Id': subjectId },
  loadFailureMode: 'throw',
  mutationFailureMode: 'throw',
  retry: { attempts: 2, baseDelayMs: 250 },
});

The maintained layout-example.tsx shows stable capability storage, cookie fallback, idempotency keys, hydration without a false success state, and observable persistence failures.

Express recipes

Copy the complete src/express directory so routes and the shared request-context seam stay together. Connect verified auth middleware state inresolveVerifiedNDPRActor before exposing staff routes.

import express from 'express';
import { createNDPRRouter } from './ndpr/express';

const app = express();
app.use(express.json());
app.use(yourVerifiedSessionMiddleware());
app.use('/api/ndpr', createNDPRRouter());

The default actor resolver is intentionally empty. Mounting the router without replacing it leaves staff routes unavailable rather than trusting request data.

Production checklist

  • Pin toolkit and recipe versions and review copied source in your repository.
  • Configure a server-only tenant ID and test cross-tenant isolation.
  • Connect verified actor resolution and test anonymous, authenticated subject, staff, and forbidden paths.
  • Keep record and audit writes atomic and test rollback behavior.
  • Exercise consent revocation, DSR intake, breach updates, DPIA lifecycle changes, and ROPA completeness in staging.
  • Review retention, backups, access controls, incident escalation, and current NDPC requirements with your DPO or counsel.

Scaffold or audit

npm create ndpr@latest
npx ndpr audit --init
npx ndpr audit --min-score 80

Generated request-context files also fail closed until connected to your authentication provider. See the Compliance Audit CLI guide for CI configuration.