@holdify/nextjs
Next.js middleware for Holdify.
Installation
bash
npm install @holdify/nextjsQuick start
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
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
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:
Authorization: Bearer <key>headerx-api-key: <key>header?api_key=<key>query parameter
Route-specific 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
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