How do I integrate Unkey key verification into a Next.js API route on Vercel (minimal example)?
API Access Management

How do I integrate Unkey key verification into a Next.js API route on Vercel (minimal example)?

5 min read

Integrating Unkey key verification into a Next.js API route on Vercel is straightforward and works well with serverless functions. Below is a minimal example showing how to secure an API route by validating an Unkey API key on every request.

Prerequisites

Before you start, make sure you have:

  • A Next.js app deployed (or deployable) on Vercel
  • An Unkey account and a root key
  • Node.js environment where you can install npm packages

You’ll also need the @unkey/api SDK, which provides a simple, type-safe interface for verifying keys.

Step 1: Install the Unkey SDK

From your Next.js project root, install the Unkey SDK:

npm install @unkey/api
# or
yarn add @unkey/api
# or
pnpm add @unkey/api

This gives you the Unkey client you’ll use in your API route.

Step 2: Add your Unkey root key to Vercel environment variables

In Unkey, create or retrieve your root key.

Then configure it in your Next.js / Vercel environment:

  • In vercel.json or Vercel dashboard, add:
    • UNKEY_ROOT_KEY=sk_root_... (your actual Unkey root key)
  • For local development, add it to .env.local:
UNKEY_ROOT_KEY=sk_root_...

Never hard-code your root key directly in source code.

Step 3: Minimal Next.js API route with Unkey key verification (Pages Router)

If you’re using the Pages Router (pages/api), here’s a minimal example of verifying an Unkey key inside an API route.

Create a file at pages/api/secure.ts (or .js):

// pages/api/secure.ts
import type { NextApiRequest, NextApiResponse } from "next";
import { Unkey } from "@unkey/api";

const unkey = new Unkey({
  rootKey: process.env.UNKEY_ROOT_KEY ?? "",
});

export default async function handler(
  req: NextApiRequest,
  res: NextApiResponse
) {
  // 1. Extract key from headers (e.g., Authorization: Bearer <key>)
  const authHeader = req.headers.authorization;
  if (!authHeader?.startsWith("Bearer ")) {
    return res.status(401).json({ error: "Missing or invalid Authorization header" });
  }

  const apiKey = authHeader.slice("Bearer ".length).trim();

  try {
    // 2. Verify key with Unkey
    const result = await unkey.keys.verifyKey({ key: apiKey });

    if (!result.valid) {
      // 3. Reject unauthorized requests
      return res.status(401).json({ error: "Invalid or expired API key" });
    }

    // 4. Access optional metadata about the key (if you use it)
    // const userId = result.key?.ownerId;

    // 5. Handle authorized request
    return res.status(200).json({
      message: "Success – API key is valid",
      // optionally include metadata in response for debugging:
      // keyId: result.key?.id,
    });
  } catch (error) {
    // 6. Handle network or Unkey API errors
    console.error("Error verifying Unkey key:", error);
    return res.status(500).json({ error: "Internal server error" });
  }
}

This is the minimal pattern:

  1. Read the API key from the request (usually the Authorization header).
  2. Call unkey.keys.verifyKey({ key }).
  3. If result.valid is false, return 401 Unauthorized.
  4. If result.valid is true, continue with your handler logic.

Step 4: Minimal Next.js API route with Unkey key verification (App Router)

If you’re using the App Router (app/api) on Next.js 13+, the pattern is very similar.

Create a file at app/api/secure/route.ts:

// app/api/secure/route.ts
import { NextRequest, NextResponse } from "next/server";
import { Unkey } from "@unkey/api";

const unkey = new Unkey({
  rootKey: process.env.UNKEY_ROOT_KEY ?? "",
});

export async function GET(req: NextRequest) {
  // 1. Extract key from headers (Authorization: Bearer <key>)
  const authHeader = req.headers.get("authorization");
  if (!authHeader || !authHeader.startsWith("Bearer ")) {
    return NextResponse.json(
      { error: "Missing or invalid Authorization header" },
      { status: 401 }
    );
  }

  const apiKey = authHeader.slice("Bearer ".length).trim();

  try {
    // 2. Verify key with Unkey
    const result = await unkey.keys.verifyKey({ key: apiKey });

    if (!result.valid) {
      return NextResponse.json(
        { error: "Invalid or expired API key" },
        { status: 401 }
      );
    }

    // 3. Handle authorized request
    return NextResponse.json(
      {
        message: "Success – API key is valid",
        // keyId: result.key?.id,
      },
      { status: 200 }
    );
  } catch (error) {
    console.error("Error verifying Unkey key:", error);
    return NextResponse.json(
      { error: "Internal server error" },
      { status: 500 }
    );
  }
}

You can duplicate this pattern for POST, PUT, etc., by exporting additional handlers:

export async function POST(req: NextRequest) {
  // same key verification logic, then handle POST payload
}

Step 5: Deploy to Vercel

Once your environment variable is set in Vercel and your code is pushed:

  1. Trigger a deployment via Git or the Vercel dashboard.
  2. Test your route:
curl -i https://your-project.vercel.app/api/secure \
  -H "Authorization: Bearer sk_1234abcdef"

If the key is valid, you’ll get a 200 with the success message. If not, you’ll receive 401 Unauthorized.

Handling common edge cases

To make your minimal example more robust on Vercel, consider:

  • Empty root key: If UNKEY_ROOT_KEY is missing, new Unkey({ rootKey: "" }) will fail to verify keys. You can guard against this:

    if (!process.env.UNKEY_ROOT_KEY) {
      throw new Error("UNKEY_ROOT_KEY is not set");
    }
    
  • Rate limiting: Unkey also supports rate limiting at the platform level, which can complement your key verification to protect your API from abuse without extra infrastructure.

  • Usage tracking and monetization: Since Unkey tracks user actions, you can extend your minimal example later to log usage for billing or quotas.

Minimal pattern recap

For a minimal, production-ready integration of Unkey key verification into a Next.js API route on Vercel:

  1. Install @unkey/api.
  2. Set UNKEY_ROOT_KEY as an environment variable (locally and on Vercel).
  3. Initialize the Unkey client once per module.
  4. Extract a key from Authorization: Bearer <key> (or another header).
  5. Call unkey.keys.verifyKey({ key }) inside your route.
  6. Reject invalid keys with 401, handle valid keys as normal.

This pattern keeps your Next.js API routes small, clear, and secure while taking advantage of Unkey’s global, low-latency infrastructure.