Docs SDK @holdify/nextjs

@holdify/nextjs

Next.js middleware for Holdify.

Installation

Terminal
bash
npm install @holdify/nextjs

Quick start

middleware.ts
typescript
// middleware.ts (in project root)
import { createHoldifyMiddleware } from '@holdify/nextjs';
import { NextRequest } from 'next/server';

const holdify = createHoldifyMiddleware({
  apiKey: process.env.HOLDIFY_PROJECT_KEY!,
});

export async function middleware(request: NextRequest) {
  // Only protect /api routes
  if (request.nextUrl.pathname.startsWith('/api')) {
    return holdify(request);
  }
}

export const config = {
  matcher: '/api/:path*',
};

Accessing result in API routes

app/api/data/route.ts
typescript
// app/api/data/route.ts
import { getHoldifyResult } from '@holdify/nextjs';
import { NextRequest } from 'next/server';

export async function GET(request: NextRequest) {
  const holdify = getHoldifyResult(request);

  if (!holdify) {
    return Response.json({ error: 'Not verified' }, { status: 401 });
  }

  return Response.json({
    data: 'protected',
    remaining: holdify.remaining,
    plan: holdify.plan,
  });
}

Configuration

Middleware options
typescript
createHoldifyMiddleware({
  // Required
  apiKey: process.env.HOLDIFY_PROJECT_KEY!,

  // Optional: Custom API URL
  baseUrl: 'https://api.holdify.io',

  // Optional: Custom key extraction
  getKey: (req) => {
    return req.headers.get('x-custom-key') || undefined;
  },

  // Optional: Custom error response
  onError: (error, req) => {
    return NextResponse.json(
      { error: error.message },
      { status: error.statusCode || 500 }
    );
  },
});

Key extraction

By default looks for:

  1. Authorization: Bearer <key> header
  2. x-api-key: <key> header
  3. ?api_key=<key> query parameter

Route-specific protection

Selective protection
typescript
// middleware.ts
export async function middleware(request: NextRequest) {
  const path = request.nextUrl.pathname;

  // Only protect specific routes
  if (path.startsWith('/api/protected')) {
    return holdify(request);
  }

  // Skip public routes
  if (path.startsWith('/api/public')) {
    return NextResponse.next();
  }
}

With App Router

Entitlement check
typescript
// app/api/data/route.ts
import { getHoldifyResult } from '@holdify/nextjs';

export async function GET(request: NextRequest) {
  const { remaining, entitlements } = getHoldifyResult(request)!;

  // Check feature access
  if (!entitlements?.includes('feature:data-export')) {
    return Response.json(
      { error: 'Upgrade required' },
      { status: 403 }
    );
  }

  return Response.json({ data: 'export data' });
}

Rate limit headers

Automatically sets:

  • X-RateLimit-Limit
  • X-RateLimit-Remaining
  • X-RateLimit-Reset