> ## Documentation Index
> Fetch the complete documentation index at: https://unkey.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# @unkey/nextjs

> Use the @unkey/nextjs SDK to protect Next.js API routes and server actions with Unkey API key authentication. Includes withUnkey wrapper.

The official Next.js SDK for Unkey. Use this within your [route handlers](https://nextjs.org/docs/app/building-your-application/routing/route-handlers) as a simple, type-safe way to verify API keys.

<Card icon="github" title="github.com/unkeyed/sdks/tree/main/nextjs" href="https://github.com/unkeyed/sdks/tree/main/nextjs" />

## Install

<CodeGroup>
  ```bash npm theme={"theme":"kanagawa-wave"}
   npm install @unkey/nextjs
  ```

  ```bash pnpm theme={"theme":"kanagawa-wave"}
   pnpm add @unkey/nextjs
  ```

  ```bash yarn theme={"theme":"kanagawa-wave"}
   yarn add @unkey/nextjs
  ```

  ```bash bun theme={"theme":"kanagawa-wave"}
   bun add @unkey/nextjs
  ```
</CodeGroup>

Protecting API routes is as simple as wrapping them with the `withUnkey` handler:

```ts theme={"theme":"kanagawa-wave"}
import { NextRequestWithUnkeyContext, withUnkey } from "@unkey/nextjs";

export const POST = withUnkey(
  async (req: NextRequestWithUnkeyContext) => {
    // The key has already been verified at this point
    // Access verification details via req.unkey.data
    if (!req.unkey.data.valid) {
      return new Response("Unauthorized", { status: 401 });
    }

    // Your API logic here
    return Response.json({
      message: "Hello!",
      keyId: req.unkey.data.keyId,
      // If you set an externalId when creating the key:
      externalId: req.unkey.data.identity?.externalId,
    });
  },
  { rootKey: process.env.UNKEY_ROOT_KEY! },
);
```

### What's in `req.unkey`?

The `req.unkey.data` object contains the verification result:

| Field         | Type        | Description                                                        |
| ------------- | ----------- | ------------------------------------------------------------------ |
| `valid`       | `boolean`   | Whether the key passed all checks                                  |
| `code`        | `string`    | Status code (`VALID`, `NOT_FOUND`, `RATE_LIMITED`, etc.)           |
| `keyId`       | `string`    | The key's unique identifier                                        |
| `name`        | `string?`   | Human-readable name of the key                                     |
| `meta`        | `object?`   | Custom metadata associated with the key                            |
| `expires`     | `number?`   | Unix timestamp (in milliseconds) when the key will expire (if set) |
| `credits`     | `number?`   | Remaining uses (if usage limits set)                               |
| `enabled`     | `boolean`   | Whether the key is enabled                                         |
| `roles`       | `string[]?` | Roles attached to the key                                          |
| `permissions` | `string[]?` | Permissions attached to the key                                    |
| `identity`    | `object?`   | Identity info if `externalId` was set when creating the key        |
| `ratelimits`  | `object[]?` | Rate limit states (if rate limiting configured)                    |

<Note>
  Access these via `req.unkey.data.valid`, `req.unkey.data.keyId`, etc.
</Note>

If you want to customize how `withUnkey` processes incoming requests, you can do so as follows:

### `getKey`

By default, withUnkey will look for a bearer token located in the `authorization` header. If you want to customize this, you can do so by passing a getter
in the configuration object:

```ts theme={"theme":"kanagawa-wave"}
export const GET = withUnkey(
  async (req) => {
    // ...
  },
  {
    rootKey: process.env.UNKEY_ROOT_KEY!,
    getKey: (req) => new URL(req.url).searchParams.get("key"),
  },
);
```

### `onError`

You can specify custom error handling. By default errors will be logged to the console, and `withUnkey` will return a NextResponse with status 500.

```ts theme={"theme":"kanagawa-wave"}
export const GET = withUnkey(
  async (req) => {
    // ...
  },
  {
    rootKey: process.env.UNKEY_ROOT_KEY!,
    onError: async (req, res) => {
      await analytics.trackEvent(`Error ${res.code}: ${res.message}`);
      return new NextResponse("Unkey error", { status: 500 });
    },
  },
);
```

### `handleInvalidKey`

Specify what to do if Unkey reports that your key is invalid.

```ts theme={"theme":"kanagawa-wave"}
export const GET = withUnkey(
  async (req) => {
    // ...
  },
  {
    rootKey: process.env.UNKEY_ROOT_KEY!,
    handleInvalidKey: (req, res) => {
      return new Response("Unauthorized", { status: 401 });
    },
  },
);
```
