Skip to main content

Usage statistics & metrics

The Next.js SDK can optionally batch and send:

  • usage statistics (feature checks + usage / views)
  • metrics (measurements / counters / observations)
RuntimePackageTransport
Server (Node RSC, Server Actions, Route Handlers)@ops-ai/nextjs-toggly-serverOptional gRPC (@grpc/grpc-js + @grpc/proto-loader)
Edge (Middleware / Edge Runtime)@ops-ai/nextjs-toggly-edgeHTTPS JSON to api/usage/stats and api/metrics — no Node gRPC

Both use metricsBaseUrl (default https://app.toggly.io/) for the telemetry gateway. That host is separate from definitions (baseUri / https://definitions.toggly.io).

With an appKey, usage and metrics default on. Opt out with config flags or TOGGLY_DISABLE_TELEMETRY=1 (authoritative on Next server and edge — wins over explicit true).

Browser companion​

@ops-ai/nextjs-toggly-client 1.5.0 adds a separate compact browser reporter. Its metricsBaseUrl defaults to https://metrics.toggly.io; it does not use or replace the trusted server and edge gateway above.

'use client'
import { useToggly } from '@ops-ai/nextjs-toggly-client'

export function CheckoutTelemetry({
onOpenPricing,
}: {
onOpenPricing: () => void
}) {
const { telemetry } = useToggly()

const handleCheckout = async () => {
telemetry.recordUsage('Checkout')
telemetry.incrementCounter('orders')
telemetry.setGauge('cartItems', 3)
await telemetry.flushTelemetry()
}

const handleOpenPricing = async () => {
onOpenPricing()
telemetry.recordView('PricingTable', 'control')
await telemetry.flushTelemetry()
}

return (
<>
<button onClick={handleCheckout}>Checkout</button>
<button onClick={handleOpenPricing}>Open pricing</button>
</>
)
}

Render this component beneath TogglyProvider. The pricing handler records a view only when the application actually opens that view; never record an explicit view from component render.

Provider configuration accepts enableTelemetry, enableUsageTracking, enableMetrics, metricsBaseUrl, and telemetryFlushIntervalMs. The provider owns the reporter and releases it on final unmount. SSR and build imports stay silent. Legacy browser recordUsage(feature, identity?, variant?) and recordView keep identity as the second argument and use only the third as the variant. Legacy browser measure and observe are diagnostic no-ops; use telemetry.incrementCounter or telemetry.setGauge when those semantics fit. See the frontend telemetry contract.

Session identity​

Set instanceId in TogglyProvider config. Inside a component, obtain setContext from useToggly() and await it in an interaction callback: setContext({ identity: 'user-123', instanceId: 'minted-token' }). At logout use { identity: '', instanceId: '', groups: [], claims: {} }. Supplying identity without a token also clears the old token. This updates the browser owner; trusted server request context is separate. See session attribution.

Server (optional gRPC)​

Install optional gRPC peers when you want telemetry to leave the process:

npm install @grpc/grpc-js @grpc/proto-loader

Without those packages, the server still evaluates flags. If usage/metrics are enabled, it logs a warning and skips sending.

Enable / configure​

app/layout.tsx
import { initServerToggly } from '@ops-ai/nextjs-toggly-server'

await initServerToggly({
appKey: process.env.TOGGLY_APP_KEY!,
environment: 'Production',
// Defaults: enableUsageTracking / enableMetrics true when appKey is set
enableUsageTracking: true,
enableMetrics: true,
metricsBaseUrl: 'https://app.toggly.io/',
usageFlushInterval: 60_000, // ms; 0 disables the timer
metricsFlushInterval: 60_000,
})
OptionDefaultNotes
enableUsageTrackingtrue when appKey is setFeature check auto-record + recordServerUsage / recordServerView
enableMetricstrue when appKey is setmeasure / incrementCounter / observe
metricsBaseUrlhttps://app.toggly.io/gRPC target host (not definitions)
usageFlushInterval / metricsFlushInterval60000Periodic flush; 0 disables timers
instanceName / appVersionoptionalReported on payloads

Transport defaults to gRPC (telemetryTransport: 'grpc'). Process signal handlers flush on shutdown when telemetry is active.

Record usage and metrics​

Feature evaluations auto-record checks when usage tracking is on. For explicit “used” / “viewed” events and business metrics:

import {
recordServerUsage,
recordServerView,
measureServerMetric,
incrementServerCounter,
observeServerMetric,
flushServerTelemetry,
closeServerToggly,
} from '@ops-ai/nextjs-toggly-server'

// After a meaningful interaction (e.g. button click handled in a Server Action)
recordServerUsage('CheckoutV2', userId)
recordServerView('PricingTable', userId)

measureServerMetric('checkout.latency_ms', 12.3, { feature: 'CheckoutV2' })
incrementServerCounter('checkout.clicks', 1, { feature: 'CheckoutV2' })
observeServerMetric('cart.items', cart.size, { feature: 'CheckoutV2' })

await flushServerTelemetry()
// On process shutdown:
await closeServerToggly()

You can also call recordUsage / recordView / measure / incrementCounter / observe / flushTelemetry on the client from useServerToggly() / getServerToggly().

Edge (HTTPS only)​

Edge Middleware and the Edge Runtime cannot open Node gRPC sockets. @ops-ai/nextjs-toggly-edge posts JSON to:

  • {metricsBaseUrl}api/usage/stats
  • {metricsBaseUrl}api/metrics

Default flush intervals on the edge client are 0 (no background timer). Schedule a flush with the platform waitUntil so isolates can exit without dropping batches.

middleware.ts
import {
createFeatureMiddleware,
getEdgeToggly,
} from '@ops-ai/nextjs-toggly-edge'
import type { NextRequest } from 'next/server'

const featureMiddleware = createFeatureMiddleware({
appKey: process.env.TOGGLY_APP_KEY!,
environment: 'Production',
enableUsageTracking: true,
enableMetrics: true,
metricsBaseUrl: 'https://app.toggly.io/',
// Edge defaults: no periodic flush timers — schedule or await a flush
usageFlushInterval: 0,
metricsFlushInterval: 0,
})

export async function middleware(request: NextRequest) {
const response = await featureMiddleware(request, {
featureKey: 'beta-access',
redirectTo: '/waitlist',
})

const client = getEdgeToggly()
// Prefer scheduleFlush(platformWaitUntil) on Cloudflare / Vercel so the
// response is not blocked. Fall back to awaiting flushTelemetry().
await client?.flushTelemetry()

return response
}

Manual APIs on TogglyEdgeClient (from initEdgeToggly / getEdgeToggly):

  • recordUsage / recordView
  • measure / incrementCounter / observe
  • flushTelemetry() / scheduleFlush(waitUntil) / close()

Helpers: flushEdgeTelemetry(), closeEdgeToggly().

Same pattern as the standalone Cloudflare Workers integration: HTTPS telemetry, flush via waitUntil, never block flag evaluation on a hard send failure.

Kill switch​

TOGGLY_DISABLE_TELEMETRY=1

On Next server and edge, this env flag disables usage and metrics even when enableUsageTracking / enableMetrics are explicitly true. Useful in local/CI without changing app config.

See also​