🔥 Pro plan free — limited timeYour APIs are failing right now and you don't know it.5 min setup · No credit card · No config files
Start catching failures →
FeaturesPricingDocsBlogLog inStart free — no card

@nurbak/watch

Lightweight monitoring SDK for Next.js API routes and Server Actions.

npm versionNext.jsLicense: MIT

Nurbak Watch monitors every Next.js API route and Server Action automatically — no external pings, no agents, no YAML. Health checks run inside your server process, catching issues that external monitors miss.

Quick Start

The fastest way to get started is with the CLI:

npx @nurbak/watch init

The CLI will automatically:

  1. Detect your Next.js project (TypeScript/JavaScript, src/ directory, package manager)
  2. Ask for your API key
  3. Create instrumentation.ts with the SDK initialized
  4. Create middleware.ts with the monitoring wrapper
  5. Add your API key to .env.local
  6. Install the package

Non-interactive mode (for CI):

npx @nurbak/watch init --key nw_test_your_key_here

That's it. Start your dev server and events will appear in your dashboard within 30 seconds.

Manual Installation

1. Install the package

# npm
npm install @nurbak/watch

# yarn
yarn add @nurbak/watch

# pnpm
pnpm add @nurbak/watch

# bun
bun add @nurbak/watch

2. Create instrumentation.ts

This file hooks into the Next.js instrumentation API and initializes the SDK when your server starts.

App Router with src/ directory — create src/instrumentation.ts:

import { initWatch } from '@nurbak/watch'

export async function register() {
  initWatch({
    apiKey: process.env.NODE_ENV === 'production'
      ? process.env.NURBAK_WATCH_KEY_LIVE!
      : process.env.NURBAK_WATCH_KEY_TEST!,
  })
}

Without src/ directory — create instrumentation.ts at the project root.

JavaScript projects — use the same code in instrumentation.js (remove the ! non-null assertions).

3. Create middleware.ts

The middleware captures App Router API route requests, including latency, status codes, and response metadata.

import { withNurbakMiddleware } from '@nurbak/watch'

export default withNurbakMiddleware()

export const config = { matcher: '/api/:path*' }

4. Add your API key

Add your keys to .env.local:

# For development
NURBAK_WATCH_KEY_TEST=nw_test_your_key_here

# For production
NURBAK_WATCH_KEY_LIVE=nw_live_your_key_here

5. Enable instrumentation hook (Next.js < 15 only)

If you’re on Next.js 13.4–14.x, enable the experimental hook in next.config.js:

/** @type {import('next').NextConfig} */
const nextConfig = {
  experimental: {
    instrumentationHook: true,
  },
}
module.exports = nextConfig

Next.js 15+ has instrumentation enabled by default — no config change needed.

Configuration

Pass options to initWatch() to customize the SDK:

import { initWatch } from '@nurbak/watch'

export async function register() {
  initWatch({
    apiKey: process.env.NURBAK_WATCH_KEY_LIVE!,
    enabled: true,
    debug: false,
    sampleRate: 1.0,
    ignorePaths: ['/api/health'],
    flushInterval: 5000,
    maxBatchSize: 100,
    ingestUrl: 'https://ingestion.nurbak.com',
  })
}
OptionTypeDefaultDescription
apiKeystringRequired. Your Nurbak Watch API key.
enabledbooleantrueSet to false to disable. Automatically disabled in NODE_ENV=test.
debugbooleanfalseEnables verbose console logging.
sampleRatenumber1.0Fraction of requests to monitor (0.0–1.0). Useful for high-traffic apps.
ignorePathsstring[][]Path prefixes to exclude from monitoring.
flushIntervalnumber5000Milliseconds between automatic queue flushes.
maxBatchSizenumber100Maximum events per batch.
ingestUrlstringhttps://ingestion.nurbak.comOverride for self-hosted or testing.

Environment Variables

VariableDescription
NURBAK_WATCH_KEY_LIVEAPI key for production. Used when NODE_ENV=production.
NURBAK_WATCH_KEY_TESTAPI key for development/staging.
NURBAK_WATCH_KEYFallback API key if the environment-specific key is not set.
NURBAK_WATCH_DEBUGSet to "true" to enable debug logging in the middleware layer.
NURBAK_WATCH_INGEST_URLOverride the default ingest endpoint from the middleware layer.

Middleware

Basic usage

import { withNurbakMiddleware } from '@nurbak/watch'

export default withNurbakMiddleware()

export const config = { matcher: '/api/:path*' }

Wrapping an existing middleware

import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
import { withNurbakMiddleware } from '@nurbak/watch'

function myMiddleware(request: NextRequest) {
  const response = NextResponse.next()
  response.headers.set('x-custom-header', 'value')
  return response
}

export default withNurbakMiddleware(myMiddleware)

export const config = { matcher: '/api/:path*' }

The wrapper passes the request through your middleware first, then captures the response metadata and sends it to Nurbak after the response is delivered.

Custom matcher

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

What Gets Monitored

API Routes (App Router & Pages Router)

Every request to /api/* is captured via two mechanisms:

Server Actions

Server Actions are detected via the Next-Action HTTP header and captured through a fetch interceptor. The SDK resolves action names from Next.js’s server-reference-manifest.json when available.

Data captured per event

FieldDescription
methodHTTP method (GET, POST, PUT, DELETE, etc.)
pathNormalized route path (dynamic segments replaced with [id])
statusCodeHTTP status code
statusCategory2xx, 3xx, 4xx, or 5xx
durationMsRequest duration in milliseconds
responseBytesResponse body size in bytes
runtimenodejs or edge
regionVercel region (when available via VERCEL_REGION)
errorTypeError class name (on failures)
errorMessageError message, truncated to 200 chars (on failures)
actionHashServer Action hash
actionNameResolved Server Action function name

Path normalization

Dynamic segments are automatically replaced with [id] to group related routes:

How It Works

Your Next.js App ├── instrumentation.ts │ └── initWatch() │ ├── Initializes event queue with batching + retry │ ├── Patches http.Server to capture Pages Router API requests │ └── Patches global fetch to capture Server Actions │ └── middleware.ts └── withNurbakMiddleware() ├── Captures App Router API route requests ├── Measures latency and response status └── Uses after() to flush events after response is sent

Event lifecycle:

  1. A request hits your API route or Server Action
  2. The SDK captures method, path, status, latency, and error info
  3. The event is added to an in-memory queue
  4. The queue flushes to Nurbak’s ingest API every 5 seconds (configurable) or immediately in serverless
  5. Failed flushes retry up to 3 times with backoff on 429
  6. On Vercel, after() and waitUntil() ensure flushes complete before container freeze

API Reference

initWatch(config)

Initializes the SDK. Call this inside register() in your instrumentation.ts. Idempotent — calling it multiple times has no effect.

import { initWatch } from '@nurbak/watch'

initWatch({
  apiKey: process.env.NURBAK_WATCH_KEY_LIVE!,
  debug: true,
})

withNurbakMiddleware(middleware?)

Wraps your Next.js middleware to capture API route monitoring data. Returns a new middleware function.

import { withNurbakMiddleware } from '@nurbak/watch'

// Without existing middleware
export default withNurbakMiddleware()

// With existing middleware
export default withNurbakMiddleware(myMiddleware)

flush()

Manually flushes the event queue. Returns a Promise<void>.

import { flush } from '@nurbak/watch'

await flush()

getSdkStatus()

Returns the current SDK status for debugging.

import { getSdkStatus } from '@nurbak/watch'

const status = await getSdkStatus()
// { initialized: true, queueSize: 0, config: { ... } }

Types

interface NurbakWatchConfig {
  apiKey: string
  ingestUrl?: string
  enabled?: boolean
  debug?: boolean
  sampleRate?: number
  ignorePaths?: string[]
  flushInterval?: number
  maxBatchSize?: number
}

interface SdkStatus {
  initialized: boolean
  queueSize: number
  config: NurbakWatchConfig | null
}

interface ApiCallEvent {
  eventType: 'api_route' | 'server_action'
  method: string
  path: string
  statusCode: number
  statusCategory: '2xx' | '3xx' | '4xx' | '5xx'
  responseBytes: number
  startedAt: string
  durationMs: number
  runtime: 'nodejs' | 'edge'
  region?: string
  errorType?: string
  errorMessage?: string
  actionHash?: string
  actionName?: string
}

CLI Reference

npx @nurbak/watch init

Interactive setup wizard that detects your project and creates all necessary files.

npx @nurbak/watch init

Non-interactive mode (for CI or AI assistants):

npx @nurbak/watch init --key nw_test_xxxxxxxxxxxxx
FlagDescription
--key <key>API key (nw_test_* or nw_live_*). Skips the interactive prompt.
--helpShow CLI help.

Deployment

Vercel

Nurbak Watch works on Vercel out of the box. The SDK uses after() from next/server and waitUntil() to ensure events are flushed before the serverless container freezes.

Set your environment variables in the Vercel dashboard:

NURBAK_WATCH_KEY_LIVE=nw_live_xxxxxxxxxxxxx
NURBAK_WATCH_KEY_TEST=nw_test_xxxxxxxxxxxxx

Self-hosted / Docker

The SDK works identically on self-hosted Next.js. The http.Server interceptor captures all API route requests in long-running Node.js processes.

Edge Runtime

The middleware layer (withNurbakMiddleware) works in both Node.js and Edge runtimes. The instrumentation interceptor (initWatch) runs in the Node.js runtime only.

Troubleshooting

Events not appearing in the dashboard

  1. Enable debug mode: initWatch({ apiKey: '...', debug: true }) or set NURBAK_WATCH_DEBUG=true
  2. Check that your API key starts with nw_test_ or nw_live_
  3. Verify your middleware matcher includes /api/:path*
  4. On Next.js < 15, ensure instrumentationHook: true is set in next.config.js

SDK disabled in test environment

The SDK automatically disables itself when NODE_ENV=test. To override:

initWatch({ apiKey: '...', enabled: true })

Duplicate events

If you see duplicate events, ensure initWatch() is only called once in instrumentation.ts. The SDK is idempotent — multiple calls are safe but unnecessary.

Requirements

  • Next.js 13.4 or higher (App Router, Pages Router, or both)
  • Node.js 18 or higher
  • A free Nurbak account at watch.nurbak.com

Get Your API Key

  1. Go to watch.nurbak.com
  2. Create a free account
  3. Add your project
  4. Copy your API keys (nw_test_* for development, nw_live_* for production)
  5. Add them to .env.local