npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@tantainnovative/ndpr-recipes

v0.2.0

Published

Backend recipes for @tantainnovative/ndpr-toolkit — Prisma schemas, API routes, and ORM adapters for Nigeria NDPA 2023 / NDPC GAID 2025 compliance

Readme

@tantainnovative/ndpr-recipes

Backend recipes for Nigeria NDPA 2023 / NDPC GAID 2025 compliance with @tantainnovative/ndpr-toolkit.

What is this?

This package is a reference implementation — not a library to install. Copy the files you need directly into your project and adapt them to fit your architecture. Each recipe is self-contained and heavily documented.

Do not npm install this package into your project. Clone or download the files and integrate them manually.


Overview

ndpr-recipes provides everything you need to back the @tantainnovative/ndpr-toolkit with a real database. It covers two ORM families, two server frameworks, and includes complete examples for wiring it all together.

What's covered

| Coverage area | Implementation | |---|---| | Database schema | Prisma + Drizzle ORM (PostgreSQL) | | Consent persistence | Prisma adapter, Drizzle adapter | | DSR request persistence | Prisma adapter, Drizzle adapter | | Breach report persistence | Prisma adapter | | ROPA persistence | Prisma adapter | | Next.js App Router | Consent, DSR, Breach, ROPA, Compliance, Registration route handlers | | Express | Full NDPR router with consent, DSR, breach, ROPA, compliance, registration routes | | Consent middleware | Next.js edge middleware + Express middleware | | GAID 2025 (DCPMI + CAR) | /registration route — tier classification + Compliance Audit Return schedule | | Breach Article-33 readiness | Breach detail routes return which NDPC notification fields are still missing |


Available Recipes

| File | Description | |---|---| | prisma/schema.prisma | Prisma schema — all 5 NDPA compliance tables | | src/drizzle/schema.ts | Drizzle ORM schema — mirrors the Prisma schema | | src/adapters/prisma-consent.ts | Prisma StorageAdapter<ConsentSettings> | | src/adapters/prisma-dsr.ts | Prisma StorageAdapter<DSRRequest[]> | | src/adapters/prisma-breach.ts | Prisma StorageAdapter<BreachState> | | src/adapters/prisma-ropa.ts | Prisma StorageAdapter<RecordOfProcessingActivities> | | src/adapters/drizzle-consent.ts | Drizzle StorageAdapter<ConsentSettings> | | src/adapters/drizzle-dsr.ts | Drizzle StorageAdapter<DSRRequest[]> | | src/adapters/drizzle-breach.ts | Drizzle StorageAdapter<BreachState> | | src/adapters/drizzle-ropa.ts | Drizzle StorageAdapter<RecordOfProcessingActivities> | | src/adapters/drizzle-dpia.ts | Drizzle StorageAdapter<DPIAResult[]> | | src/adapters/drizzle-lawful-basis.ts | Drizzle StorageAdapter<ProcessingActivity[]> | | src/adapters/drizzle-cross-border.ts | Drizzle StorageAdapter<CrossBorderTransfer[]> | | src/nextjs/app-router/api/consent/route.ts | Next.js consent API route | | src/nextjs/app-router/api/dsr/route.ts | Next.js DSR API route | | src/nextjs/app-router/api/breach/route.ts | Next.js breach API route | | src/nextjs/app-router/api/breach/[id]/route.ts | Next.js breach detail route — returns GAID 2025 Art. 33 readiness | | src/nextjs/app-router/api/ropa/route.ts | Next.js ROPA API route | | src/nextjs/app-router/api/compliance/route.ts | Next.js compliance score API route | | src/nextjs/app-router/api/registration/route.ts | Next.js DCPMI tier + CAR schedule route (GAID 2025) | | src/nextjs/app-router/middleware.ts | Next.js consent gate middleware | | src/nextjs/app-router/layout-example.tsx | Full wiring example for App Router | | src/express/index.ts | Express router factory — mounts all routes | | src/express/routes/consent.ts | Express consent router | | src/express/routes/dsr.ts | Express DSR router | | src/express/routes/breach.ts | Express breach router — GET /:id returns GAID 2025 Art. 33 readiness | | src/express/routes/ropa.ts | Express ROPA router | | src/express/routes/compliance.ts | Express compliance score router | | src/express/routes/registration.ts | Express DCPMI tier + CAR schedule router (GAID 2025) | | src/express/middleware/consent-check.ts | Express consent gate middleware |


Quick Start

1. Copy the database schema

Prisma:

# Copy into your project
cp packages/ndpr-recipes/prisma/schema.prisma prisma/schema.prisma

Drizzle:

# Copy the schema file
cp packages/ndpr-recipes/src/drizzle/schema.ts src/db/ndpr-schema.ts

2. Set up the database connection

# .env
DATABASE_URL="postgresql://user:password@localhost:5432/myapp_dev"

3. Run migrations

Prisma:

npx prisma migrate dev --name init-ndpr-tables
npx prisma generate

Drizzle:

npx drizzle-kit push
# or generate a migration file:
npx drizzle-kit generate
npx drizzle-kit migrate

4. Copy and wire the adapters

Pick the adapter for your ORM (see sections below), copy it into your project, and pass it to the relevant toolkit hook.


Prisma Adapters

The adapters in src/adapters/prisma-*.ts implement the StorageAdapter<T> interface from @tantainnovative/ndpr-toolkit. Copy them alongside your Prisma client and pass them to the corresponding toolkit hook.

Consent adapter

Follows the immutable-audit pattern required by NDPA Section 25: records are never deleted, and revocation sets revokedAt on the existing row.

import { PrismaClient } from '@prisma/client';
import { useConsent } from '@tantainnovative/ndpr-toolkit';
import { prismaConsentAdapter } from './adapters/prisma-consent';

const prisma = new PrismaClient();

function ConsentBanner() {
  const adapter = prismaConsentAdapter(prisma, session.userId);
  const { settings, updateConsent } = useConsent({ adapter });
  // ...
}

DSR adapter

import { prismaDSRAdapter } from './adapters/prisma-dsr';

const adapter = prismaDSRAdapter(prisma, session.user.email);
// Pass to useDSR({ adapter }) or call adapter.save(requests) in a route handler

Breach adapter

import { prismaBreachAdapter } from './adapters/prisma-breach';

const adapter = prismaBreachAdapter(prisma);
// Pass to useBreach({ adapter })

ROPA adapter

Organisation metadata (name, DPO contact, address) is not stored in the database — supply it when constructing the adapter.

import { prismaROPAAdapter } from './adapters/prisma-ropa';

const adapter = prismaROPAAdapter(prisma, {
  organizationName: process.env.ORG_NAME!,
  organizationContact: process.env.DPO_EMAIL!,
  organizationAddress: process.env.ORG_ADDRESS!,
  ndpcRegistrationNumber: process.env.NDPC_REG_NUMBER,
});
// Pass to useROPA({ adapter })

Drizzle Adapters

The adapters in src/adapters/drizzle-*.ts use the same StorageAdapter<T> interface but target a Drizzle db instance instead of Prisma. The schema lives in src/drizzle/schema.ts.

Setup

pnpm add drizzle-orm pg @paralleldrive/cuid2
pnpm add -D drizzle-kit @types/pg
// src/db.ts
import { drizzle } from 'drizzle-orm/node-postgres';
import { Pool } from 'pg';
import * as schema from './drizzle/schema';

const pool = new Pool({ connectionString: process.env.DATABASE_URL });
export const db = drizzle(pool, { schema });

Consent adapter

import { drizzleConsentAdapter } from './adapters/drizzle-consent';

const adapter = drizzleConsentAdapter(db, session.userId);
const { settings, updateConsent } = useConsent({ adapter });

DSR adapter

import { drizzleDSRAdapter } from './adapters/drizzle-dsr';

const adapter = drizzleDSRAdapter(db, session.user.email);
const { requests, submitRequest } = useDSR({ adapter });

Next.js Integration

App Router route handlers

Copy the API routes from src/nextjs/app-router/api/ into your project's app/api/ directory:

# Consent management
cp src/nextjs/app-router/api/consent/route.ts app/api/consent/route.ts

# Data subject rights
cp src/nextjs/app-router/api/dsr/route.ts app/api/dsr/route.ts

# Breach reports
cp src/nextjs/app-router/api/breach/route.ts app/api/breach/route.ts

# ROPA
cp src/nextjs/app-router/api/ropa/route.ts app/api/ropa/route.ts

# Compliance score
cp src/nextjs/app-router/api/compliance/route.ts app/api/compliance/route.ts

Each route is fully documented with its HTTP methods, query params, and body shape at the top of the file.

Consent middleware (route protection)

Protect any route that requires a specific consent type:

// app/api/email/marketing/route.ts
import { consentMiddleware } from '@/ndpr/middleware';

export async function POST(req: NextRequest) {
  const guard = await consentMiddleware(req, 'marketing');
  if (guard) return guard; // 403 if consent not granted

  // Proceed — subject has consented to marketing
}

Or use the higher-order wrapper:

import { withConsent } from '@/ndpr/middleware';

export const POST = withConsent('marketing', async (req) => {
  // marketing consent guaranteed here
});

Full layout example

See src/nextjs/app-router/layout-example.tsx for a complete wiring example. Copy it to components/ndpr-layout.tsx and add it to your root layout:

// app/layout.tsx
import NDPRLayout from '@/components/ndpr-layout';

export default async function RootLayout({ children }) {
  const session = await getServerSession();
  return (
    <html lang="en">
      <body>
        <NDPRLayout userId={session?.user?.id}>
          {children}
        </NDPRLayout>
      </body>
    </html>
  );
}

Express Integration

Mount the full compliance router

import express from 'express';
import cookieParser from 'cookie-parser';
import { createNDPRRouter } from './ndpr/express';

const app = express();
app.use(express.json());
app.use(cookieParser()); // required for consent cookie fallback

// Mount all NDPR compliance routes under /api/ndpr
app.use('/api/ndpr', createNDPRRouter());

This mounts:

| Route | Module | |---|---| | GET/POST/DELETE /api/ndpr/consent | Consent management | | GET/POST/PATCH /api/ndpr/dsr | Data subject rights | | GET/POST/PATCH /api/ndpr/breach | Breach notification | | GET/POST/PATCH /api/ndpr/ropa | Record of Processing Activities | | GET /api/ndpr/compliance | Compliance score | | GET /api/ndpr/registration | DCPMI tier + CAR schedule (GAID 2025) |

Consent middleware (route protection)

import { requireConsent } from './ndpr/express/middleware/consent-check';

// Require marketing consent before sending a marketing email
app.post('/email/marketing', requireConsent('marketing'), sendEmailHandler);

// Require multiple consents — all must be granted
import { requireAllConsents } from './ndpr/express/middleware/consent-check';
app.post('/profile/analytics', requireAllConsents(['analytics', 'functional']), handler);

Use individual routers (granular mounting)

import { consentRouter, dsrRouter } from './ndpr/express';

// Mount only the routes you need
app.use('/api/consent', consentRouter);
app.use('/api/dsr', dsrRouter);

Full Example

Below is the complete layout-example.tsx showing the toolkit wired up in a Next.js App Router layout with a server-backed consent adapter:

'use client';

import React from 'react';
import { NDPRProvider } from '@tantainnovative/ndpr-toolkit/core';
import { NDPRConsent } from '@tantainnovative/ndpr-toolkit/presets';
import { apiAdapter } from '@tantainnovative/ndpr-toolkit/adapters';

export default function NDPRLayout({
  children,
  userId,
}: {
  children: React.ReactNode;
  userId?: string;
}) {
  const subjectId = userId ?? 'anonymous';

  return (
    <NDPRProvider
      organizationName="Your Company"
      dpoEmail="[email protected]"
    >
      {children}

      <NDPRConsent
        adapter={apiAdapter(`/api/consent?subjectId=${subjectId}`)}
      />
    </NDPRProvider>
  );
}

The apiAdapter hits your /api/consent route handler (from src/nextjs/app-router/api/consent/route.ts), which persists consent to PostgreSQL via Prisma or Drizzle.


GAID 2025 — DCPMI registration & breach readiness

The NDPC's General Application and Implementation Directive (GAID) 2025 added obligations the original recipes predate. Two recipes cover them, both built on the toolkit's React-free /server utilities (no extra database tables needed).

DCPMI tier + Compliance Audit Return (/registration)

classifyDCPMI derives your registration tier and annual fee from the number of data subjects you process in a six-month window; generateComplianceAuditReturn derives the filing schedule for those that must file:

  • UHL (> 5,000 subjects) — ₦250,000/yr, files a CAR annually
  • EHL (1,000–5,000) — ₦100,000/yr, files a CAR annually
  • OHL (200–999) — ₦10,000/yr, renews registration (no CAR)
// GET /api/registration?dataSubjects=6200&commencementDate=2025-01-15
import { classifyDCPMI, generateComplianceAuditReturn } from '@tantainnovative/ndpr-toolkit/server';

const classification = classifyDCPMI({ dataSubjectsInSixMonths: 6200 });
const auditReturn = generateComplianceAuditReturn({
  commencementDate: '2025-01-15',
  tier: classification.tier, // CAR applies to UHL/EHL only
});

Thresholds, fees, and deadlines follow the NDPC GAID 2025 baseline and can change — verify against current NDPC guidance before relying on them.

Breach Article-33 readiness

The breach detail routes (GET /api/breach/[id] in Next.js, GET /breach/:id in Express) now return an ndpcReadiness object via assessBreachNotification — which GAID 2025 Article 33(5) notification fields are still missing and how many hours remain on the 72-hour clock — so you know what to collect before filing. Set NDPR_DPO_NAME / NDPR_DPO_EMAIL to record the contact point.


Database Schema

Tables

| Table | Description | NDPA reference | |---|---|---| | ndpr_consent_records | Immutable consent audit trail. revokedAt marks withdrawal — rows are never deleted. | §25–26 | | ndpr_dsr_requests | Data subject rights requests. Tracks type, status, and 30-day response deadline. | Part VI §34–38 | | ndpr_breach_reports | Breach incident records with 72-hour NDPC notification tracking. | §40 | | ndpr_processing_records | Record of Processing Activities (ROPA). | Accountability principle | | ndpr_audit_log | Append-only compliance event log. | §44 |

Consent immutability

The consent table follows an immutable-audit pattern: when a subject updates or withdraws consent, the old row has revokedAt set and a new row is inserted. At most one row per subjectId has revokedAt = NULL at any time. This pattern ensures the full consent history is available for regulatory inspection without requiring separate audit log queries.


NDPA Compliance References

| Module | NDPA provision | |---|---| | Consent | Sections 25–26 (lawful basis, consent withdrawal) | | Data Subject Rights | Part VI, Sections 34–38 (access, erasure, portability, etc.) | | Breach Notification | Section 40 (72-hour notification to NDPC) | | ROPA | Accountability principle; Schedule 1, Part 1 | | Audit Log | Section 44 (accountability and record-keeping) |


License

MIT