docs / gate a next.js route (app)

Gate a Next.js route (App Router)

Use ocGateFetch — it speaks the Request/Response API that App Router handlers expect.

Install

yarn add @orangecheck/gate

Minimum working example

// app/api/post/route.ts
import { ocGateFetch } from '@orangecheck/gate';
import { NextResponse } from 'next/server';

export async function POST(req: Request) {
  const decision = await ocGateFetch(req, {
    minSats: 100_000,
    minDays: 30,
    address: { from: 'header' },   // X-OC-Address
  });

  if (!decision.ok) {
    return NextResponse.json({ error: decision.reason }, { status: 403 });
  }

  // ... your handler
  return NextResponse.json({ ok: true, sats: decision.check?.sats });
}

Read the subject from a different place

// Query string
const url = new URL(req.url);
// Then use `address: { from: (r) => new URL(r.url).searchParams.get('addr') ?? undefined }`

// Or just pass it via the subject source:
ocGateFetch(req, { address: { from: 'query', name: 'addr' } });

// JSON body
ocGateFetch(req, { address: { from: 'body', path: 'user.btcAddress' } });

// Cookie
ocGateFetch(req, { address: { from: 'cookie', name: 'oc_addr' } });

Route protection via middleware

To gate many routes at once, use Next's middleware.ts:

// middleware.ts
import { NextResponse } from 'next/server';
import { ocGateFetch } from '@orangecheck/gate';

export async function middleware(req: Request) {
  const url = new URL(req.url);
  if (!url.pathname.startsWith('/api/protected/')) return NextResponse.next();

  const decision = await ocGateFetch(req, {
    minSats: 100_000,
    address: { from: 'header' },
  });

  if (!decision.ok) {
    return NextResponse.json({ error: decision.reason }, { status: 403 });
  }
  return NextResponse.next();
}

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

Edge runtime

ocGateFetch is runtime-agnostic. To run on the Edge, declare it on the route:

// app/api/post/route.ts
export const runtime = 'edge';

export async function POST(req: Request) {
  // ... same code as above
}

The gate internally calls @orangecheck/sdk's check(), which ultimately talks to the hosted verifier and Nostr relays over fetch — all Edge-compatible.

Further