Skip to main content
1

Set environment variables

NEXT_PUBLIC_WACHT_PUBLISHABLE_KEY=pk_test_xxx
WACHT_API_KEY=wk_live_xxx
2

Wrap your app with DeploymentProvider

// app/layout.tsx
import { DeploymentProvider } from '@wacht/nextjs'

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        <DeploymentProvider publicKey={process.env.NEXT_PUBLIC_WACHT_PUBLISHABLE_KEY!}>
          {children}
        </DeploymentProvider>
      </body>
    </html>
  )
}
3

Protect routes with proxy/middleware

// proxy.ts (Next.js 16+)
import { NextResponse } from 'next/server'
import { createRouteMatcher, wachtMiddleware } from '@wacht/nextjs/server'

const isProtected = createRouteMatcher(['/dashboard(.*)', '/api/private(.*)'])

export default wachtMiddleware(async (auth, req) => {
  if (!isProtected(req)) return NextResponse.next()
  await auth.protect()
  return NextResponse.next()
}, {
  apiRoutePrefixes: ['/api', '/trpc'],
})

export const config = {
  matcher: [
    '/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)',
    '/(api|trpc)(.*)',
  ],
}
4

Use the backend client in server code (optional)

// app/api/webhooks/route.ts
import { NextResponse } from 'next/server'
import { wachtClient } from '@wacht/nextjs/server'

export async function GET() {
  const client = await wachtClient()
  const apps = await client.webhooks.listWebhookApps()
  return NextResponse.json({ apps })
}

Sign-in UI example

// app/sign-in/page.tsx
import { SignInForm } from '@wacht/nextjs'

export default function Page() {
  return <SignInForm />
}

Middleware options

type WachtMiddlewareOptions = {
  publishableKey?: string
  signInUrl?: string
  authCookieName?: string
  sessionCookieName?: string
  devSessionCookieName?: string
  clockSkewInMs?: number
  requiredIssuer?: string
  apiRoutePrefixes?: string[]
  isApiRoute?: (request: NextRequest) => boolean
  gatewayUrl?: string
}

API route classification

{ apiRoutePrefixes: ['/api', '/trpc'] }
or:
{ isApiRoute: (req) => req.nextUrl.pathname.startsWith('/internal') }