Usage statistics & metrics
The Next.js SDK can optionally batch and send:
- usage statistics (feature checks + usage / views)
- metrics (measurements / counters / observations)
| Runtime | Package | Transport |
|---|---|---|
| Server (Node RSC, Server Actions, Route Handlers) | @ops-ai/nextjs-toggly-server | Optional gRPC (@grpc/grpc-js + @grpc/proto-loader) |
| Edge (Middleware / Edge Runtime) | @ops-ai/nextjs-toggly-edge | HTTPS 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
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,
})
| Option | Default | Notes |
|---|---|---|
enableUsageTracking | true when appKey is set | Feature check auto-record + recordServerUsage / recordServerView |
enableMetrics | true when appKey is set | measure / incrementCounter / observe |
metricsBaseUrl | https://app.toggly.io/ | gRPC target host (not definitions) |
usageFlushInterval / metricsFlushInterval | 60000 | Periodic flush; 0 disables timers |
instanceName / appVersion | optional | Reported 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.
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/recordViewmeasure/incrementCounter/observeflushTelemetry()/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
- Server-side usage
- Middleware & Edge
- Node.js usage & metrics (gRPC on long-lived Node)
- Cloudflare Workers (HTTPS edge telemetry)
- Go usage & metrics