---
name: appwarden-middleware-get-started
description: >
  Install and minimally configure @appwarden/middleware for a Cloudflare or Vercel project. Covers package installation, creating an API token, wiring middleware with debug: true, verifying the heartbeat endpoint, and switching debug off after confirming the setup. Load this skill when a user is adding Appwarden to a new project for the first time.
metadata:
  type: lifecycle
  library: "@appwarden/middleware"
  library_version: "3.16.3"
requires: []
sources:
  - "appwarden/middleware:README.md"
  - "appwarden/middleware:src/index.ts"
  - "appwarden/middleware:src/schemas/use-appwarden.ts"
---

# Appwarden Middleware — Get Started

Install `@appwarden/middleware` and wire it into a Cloudflare Worker or Vercel Edge Middleware. You can do this before or after completing Appwarden organization onboarding, but you must finish onboarding to create a valid API token in the dashboard and complete the installation.

## Setup

1. Install the package:

```bash
npm install @appwarden/middleware
```

2. Create an API token in the Appwarden dashboard at `https://use.appwarden.io/?to=/settings/security` (Settings > Security). Copy it immediately — it is shown only once.

3. Store the token as a secret:
   - Cloudflare: `wrangler secret put APPWARDEN_API_TOKEN` (see https://developers.cloudflare.com/workers/configuration/secrets/)
   - Vercel: add `APPWARDEN_API_TOKEN` to Project Settings > Environment Variables

4. Wire the middleware with `debug: true` during first setup.

## How to Choose Cloudflare vs Vercel

Before wiring examples, detect the platform from the repo. If detection is ambiguous, ask the user.

### Detect Cloudflare

Cloudflare projects usually contain one or more of these signals:

```text
package.json:
  - wrangler
  - @cloudflare/workers-types
  - @astrojs/cloudflare
  - @react-router/cloudflare
  - @tanstack/react-start
  - @opennextjs/cloudflare

config files:
  - wrangler.json / wrangler.jsonc / wrangler.toml
  - react-router.config.ts with cloudflare adapter
  - astro.config.mjs with adapter: cloudflare()
  - app.config.ts referencing tanstack-start

CI files:
  - .github/workflows/*.yml with cloudflare/wrangler-action
```

### Detect supported Cloudflare frameworks

If the repo is on Cloudflare, check for one of these supported adapter patterns:

```text
- Astro Cloudflare adapter: @astrojs/cloudflare
- React Router Cloudflare adapter: @react-router/cloudflare + future.v8_middleware in react-router.config.ts
- TanStack Start Cloudflare adapter: @tanstack/react-start
- Next.js Cloudflare adapter: @opennextjs/cloudflare (Next.js 15 and earlier use middleware.ts; Next.js 16+ uses proxy.ts)
```

If none of these are present, the repo is on Cloudflare but on an unsupported framework. Recommend the standalone Cloudflare Worker approach (`appwarden-middleware-cloudflare-workflow`) instead of a framework adapter.

### Detect Vercel

Vercel projects usually contain:

```text
package.json:
  - next
  - vercel

config files:
  - vercel.json
  - middleware.ts or src/middleware.ts

CI files:
  - .github/workflows/*.yml with vercel/action-deploy
```

### When to ask the user

Ask when:

- The repo contains both Cloudflare and Vercel signals.
- The repo contains neither Cloudflare nor Vercel signals.
- The framework adapter is ambiguous (e.g., multiple adapters present).

Example prompt:

> Are you deploying on Cloudflare or Vercel? If Cloudflare, are you using Astro, React Router, TanStack Start, Next.js, or a custom worker?

## Core Patterns

All `createAppwardenMiddleware` examples below use `getAppwardenConfiguration(generatedConfig, overrides)`. This helper merges the domain configuration generated by `appwarden-link` with runtime overrides such as the API token and `debug`. Cloudflare examples import the generated config from `.appwarden/linked/middleware.json`; Vercel does not use `appwarden-link`, so they pass an empty generated config.

### Minimal Cloudflare Worker middleware

```typescript
import {
  createAppwardenMiddleware,
  getAppwardenConfiguration,
} from "@appwarden/middleware/cloudflare"
import appwardenConfig from "../.appwarden/linked/middleware.json"

export default createAppwardenMiddleware((cloudflare) =>
  getAppwardenConfiguration(appwardenConfig, {
    appwardenApiToken: cloudflare.env.APPWARDEN_API_TOKEN,
    debug: true,
  }),
)
```

`appwarden-link --fqdn=your.app` writes the generated configuration to `.appwarden/linked/middleware.json`. Keep this directory out of source control.

### Minimal Vercel Edge Middleware

```typescript
import {
  createAppwardenMiddleware,
  getAppwardenConfiguration,
} from "@appwarden/middleware/vercel"

export default createAppwardenMiddleware(
  getAppwardenConfiguration(
    {},
    {
      appwardenApiToken: process.env.APPWARDEN_API_TOKEN,
      lockPageSlug: "/maintenance",
      cacheUrl: process.env.KV_URL,
      debug: true,
    },
  ),
)

export const config = {
  matcher: [
    "/((?!api|_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)",
  ],
}
```

### Verify the heartbeat endpoint

After deploying, check `https://your.app/_appwarden/heartbeat`. A clean response contains no `configErrors` and the domain is marked verified in the dashboard at `https://use.appwarden.io/?to=/settings/security` under Settings > Security.

### Switch debug off after confirming the setup

```typescript
import {
  createAppwardenMiddleware,
  getAppwardenConfiguration,
} from "@appwarden/middleware/cloudflare"
import appwardenConfig from "../.appwarden/linked/middleware.json"

export default createAppwardenMiddleware((cloudflare) =>
  getAppwardenConfiguration(appwardenConfig, {
    appwardenApiToken: cloudflare.env.APPWARDEN_API_TOKEN,
    debug: false,
  }),
)
```

## Common Mistakes

### CRITICAL Forgetting to provide APPWARDEN_API_TOKEN as a secret

Wrong:

```typescript
export default createAppwardenMiddleware({
  appwardenApiToken: "sk_test_...",
  lockPageSlug: "/maintenance",
})
```

Correct:

```typescript
import {
  createAppwardenMiddleware,
  getAppwardenConfiguration,
} from "@appwarden/middleware/cloudflare"
import appwardenConfig from "../.appwarden/linked/middleware.json"

export default createAppwardenMiddleware((cloudflare) =>
  getAppwardenConfiguration(appwardenConfig, {
    appwardenApiToken: cloudflare.env.APPWARDEN_API_TOKEN,
    debug: true,
  }),
)
```

The API token is a secret. Hardcoding it exposes the credential and prevents Appwarden from validating the lock status.

### HIGH Setting APPWARDEN_API_TOKEN as a Wrangler var instead of a secret

Wrong:

```json
{
  "vars": { "APPWARDEN_API_TOKEN": "sk_..." }
}
```

Correct:

```bash
wrangler secret put APPWARDEN_API_TOKEN
```

Wrangler vars are visible in plain text in the Cloudflare dashboard. The token must be uploaded as a secret so it is encrypted and only available at runtime.

### HIGH Passing appwardenApiHostname to getAppwardenConfiguration

Wrong:

```typescript
import { getAppwardenConfiguration } from "@appwarden/middleware/cloudflare"
import appwardenConfig from "../.appwarden/linked/middleware.json"

getAppwardenConfiguration(appwardenConfig, {
  appwardenApiToken: env.APPWARDEN_API_TOKEN,
  appwardenApiHostname: "https://api.appwarden.io",
})
```

Correct:

```typescript
import { getAppwardenConfiguration } from "@appwarden/middleware/cloudflare"
import appwardenConfig from "../.appwarden/linked/middleware.json"

getAppwardenConfiguration(appwardenConfig, {
  appwardenApiToken: env.APPWARDEN_API_TOKEN,
})
```

The `appwardenApiHostname` override is reserved for internal testing. Agents wiring adapter config should never provide it.

### MEDIUM Leaving debug disabled during initial setup

Wrong:

```typescript
export default createAppwardenMiddleware({
  appwardenApiToken: process.env.APPWARDEN_API_TOKEN,
  lockPageSlug: "/maintenance",
})
```

Correct:

```typescript
import {
  createAppwardenMiddleware,
  getAppwardenConfiguration,
} from "@appwarden/middleware/vercel"

export default createAppwardenMiddleware(
  getAppwardenConfiguration(
    {},
    {
      appwardenApiToken: process.env.APPWARDEN_API_TOKEN,
      lockPageSlug: "/maintenance",
      cacheUrl: process.env.KV_URL,
      debug: true,
    },
  ),
)
```

Without `debug: true`, config validation failures and lock-status checks are silent, making first-time troubleshooting difficult. Disable debug only after the heartbeat is clean.

## Next Steps

- Add a lock page: see `appwarden-middleware-lock-page`.
- Harden CSP on Cloudflare: see `appwarden-middleware-csp`.
- Run pre-launch verification: see `appwarden-middleware-verify-setup`.
- If organization onboarding is not yet complete, finish it before creating the API token: see `appwarden-middleware-onboard-organization`.
