What is Statvisor?

Statvisor gives you backend API analytics and frontend performance monitoring in one SDK. Instrument your Node.js backend in one line, and drop a component into your frontend to track page views, Core Web Vitals, and visitor geography.

One-line setup

One middleware call instruments every backend route automatically.

📊

Full-stack analytics

Backend: latency, errors, volume. Frontend: page views, Web Vitals, geography.

🔒

Privacy-first

No request bodies, no PII. Country derived from IP — raw IP never stored.

Up and running in 2 minutes

The flow is the same regardless of framework: create a project, grab your API key, install the SDK, add one line. Here's the generic version:

1

Create a project in the dashboard

Go to statvisor.com/dashboard → click New project → enter your project name and the full URL of your backend (e.g. https://api.example.com). This is the origin where your API is hosted — scheme and host only, no path.
2

Copy your API key

After creating the project, your API key is shown exactly once. Copy it — it starts with vl_. Store it securely (e.g. in an environment variable).
3

Install the SDK

npm install @statvisor/sdk
4

Add the middleware

See the framework-specific guides below for the exact one-liner.
5

Verify it's working

Make any HTTP request to your API. Within a few seconds you should see it appear in the dashboard with route, method, status code, and latency.

Under the hood

The SDK wraps your framework's request lifecycle using standard middleware hooks. It records the wall-clock time before your handler runs and after it completes, capturing:

  • Route path (normalised — e.g. /users/:id, not /users/123)
  • HTTP method (GET, POST, PUT, PATCH, DELETE…)
  • Response status code
  • Duration in milliseconds
  • Timestamp (UTC)

Events are queued in memory and flushed to https://statvisor.com/api/ingest asynchronously in the background. Your request handlers are never blocked.

Statvisor never captures request bodies, response bodies, query parameters, headers, or any end-user PII. Only the metadata listed above is sent.

Node.js

Express.js Integration

Works with Express 4 and 5. Register the middleware before your routes so every request is captured.

1

Install the SDK

npm install @statvisor/sdk
2

Add the middleware

app.js
import express from "express";
import { express as statvisor } from "@statvisor/sdk";

const app = express();

// Add Statvisor BEFORE your routes
app.use(statvisor({ apiKey: process.env.STATVISOR_API_KEY }));

// Your routes
app.get("/users", (req, res) => {
  res.json({ users: [] });
});

app.listen(3000);
3

Set your environment variable

.env
STATVISOR_API_KEY=vl_your_api_key_here

The middleware must be registered before your route handlers. Place app.use(statvisor(…)) at the top of your middleware stack, before any app.use(router) calls.

Node.js

Fastify Integration

Works with Fastify v4 and v5. Register as a plugin with fastify.register().

1

Install the SDK

npm install @statvisor/sdk
2

Register the plugin

server.js
import Fastify from "fastify";
import { fastify as statvisor } from "@statvisor/sdk";

const app = Fastify({ logger: true });

// Register Statvisor plugin
await app.register(statvisor, {
  apiKey: process.env.STATVISOR_API_KEY,
});

// Your routes
app.get("/users", async (request, reply) => {
  return { users: [] };
});

await app.listen({ port: 3000 });
3

Set your environment variable

.env
STATVISOR_API_KEY=vl_your_api_key_here
Node.js

NestJS Integration

Coming soon. Native NestJS support is on the roadmap. In the meantime, you can instrument a NestJS app by using the Express adapter — NestJS uses Express under the hood by default, so registering the Express middleware in your main.ts works today.

main.ts
import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
import { express as statvisor } from "@statvisor/sdk";

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // Register Statvisor before any other middleware
  app.use(statvisor({ apiKey: process.env.STATVISOR_API_KEY }));

  await app.listen(3000);
}
bootstrap();
Node.js

Hono Integration

Works with Hono on any runtime — Node.js, Bun, Deno, Cloudflare Workers.

1

Install the SDK

npm install @statvisor/sdk
2

Add the middleware

index.ts
import { Hono } from "hono";
import { createMiddleware } from "@statvisor/sdk";

const app = new Hono();

// Add Statvisor before your routes
app.use("*", createMiddleware({ apiKey: process.env.STATVISOR_API_KEY }));

app.get("/users", (c) => {
  return c.json({ users: [] });
});

export default app;
3

Set your environment variable

# Node.js / Bun
STATVISOR_API_KEY=vl_your_api_key_here

# Cloudflare Workers — use wrangler.toml
[vars]
STATVISOR_API_KEY = "vl_your_api_key_here"
Node.js

Next.js Integration

Because Next.js API routes are serverless functions, Statvisor uses a route wrapper pattern. You initialise the SDK once in a shared file and use the returned route helper to wrap each route file's handlers.

Statvisor monitors your API routes (app/api/), not page routes or Server Actions. If your Next.js app proxies to a separate backend, instrument that backend directly with the Express or Fastify integration.

1

Install the SDK

npm install @statvisor/sdk
2

Initialise once in a shared lib file

Create a file that initialises the SDK and exports the helpers. Import from this file in every API route.

lib/statvisor.ts
import { nextjs } from "@statvisor/sdk";

export const { route, monitor } = nextjs({
  apiKey: process.env.STATVISOR_API_KEY!,
});
3

Wrap your route handlers

Use route() in each API route file. Pass the route path and an object of handlers — the returned object has the same keys ready to export.

app/api/users/route.ts
import { NextResponse } from "next/server";
import { route } from "@/lib/statvisor";

export const { GET, POST } = route("/api/users", {
  GET: async (request) => {
    const users = await db.query("SELECT * FROM users");
    return NextResponse.json({ users });
  },
  POST: async (request) => {
    const body = await request.json();
    const user = await db.create(body);
    return NextResponse.json({ user }, { status: 201 });
  },
});
4

Dynamic route segments

Pass the route pattern with the dynamic segment. The SDK records it as the pattern (e.g. /api/users/:id), not the actual URL, so routes are properly grouped in your dashboard.

app/api/users/[id]/route.ts
import { NextResponse } from "next/server";
import { route } from "@/lib/statvisor";

export const { GET } = route("/api/users/:id", {
  GET: async (request, { params }) => {
    const { id } = await params;
    const user = await db.findById(id);
    if (!user) return NextResponse.json({ error: "Not found" }, { status: 404 });
    return NextResponse.json({ user });
  },
});
5

Set your environment variable

.env.local
# Keep this server-side only — do NOT prefix with NEXT_PUBLIC_
STATVISOR_API_KEY=vl_your_api_key_here

The API key must stay server-side. Never prefix it with NEXT_PUBLIC_ — that would expose it in your client bundle.

Frontend

Browser Analytics

The browser module is framework-agnostic. It auto-tracks page views and all five Core Web Vitals (LCP, FCP, CLS, TTFB, INP) using native browser APIs — zero dependencies, zero cookies.

1

Install the SDK

npm install @statvisor/sdk
2

Copy your Frontend Key

In the dashboard, open your project and copy the Frontend Key — it starts with vl_fe_. This is a public-safe key — it is safe to include in client-side code.
3

Call initStatvisor once per page load

analytics.ts
import { initStatvisor } from "@statvisor/sdk/browser";

// Call once — tracks the current page view + Web Vitals automatically
initStatvisor({ frontendKey: "vl_fe_your_key_here" });

Call initStatvisor once per page navigation. In a traditional MPA it goes in a shared script tag. In a SPA (React, Vue, Svelte) call it inside an effect that runs on route change.

Frontend

React Integration

The @statvisor/sdk/react export provides a <StatvisorAnalytics /> component that handles initialisation automatically. Works with React 18+, Next.js App Router, and Remix.

1

Install the SDK

npm install @statvisor/sdk
2

Add to your root layout

Place the component inside <body>. It renders nothing — it only initialises tracking once on mount.

app/layout.tsx
import { StatvisorAnalytics } from "@statvisor/sdk/react";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html>
      <body>
        {children}
        <StatvisorAnalytics frontendKey="vl_fe_your_key_here" />
      </body>
    </html>
  );
}

The frontendKey starts with vl_fe_ and is intentionally public-safe — it is fine to include it directly in your source code or as a NEXT_PUBLIC_ env var. It authenticates analytics writes only, never reads.

3

For Next.js App Router — mark as a client component if needed

The component already has "use client" at the top, so you can import it directly in a Server Component layout without wrapping it.

Logging

Structured log management

Statvisor automatically captures a log entry for every request tracked by the backend SDK — no extra configuration needed. You can also emit custom structured logs from your application code using statvisor.log().

Zero-config request logs

Every request captured by the SDK is automatically logged with level, route, status code, and duration.

✍️

Custom event logs

Emit structured log events from anywhere in your code — payment failures, retries, signups, anything.

🔍

Filterable & searchable

Filter by level (info / warn / error), search by route or message, and poll for new entries in near real-time.

Logging

Emitting custom log events

Once the SDK is initialised (via any backend integration), you can call statvisor.log() anywhere in your application. Events are batched and flushed every 5 seconds.

1

Import and call log()

src/payments.ts
import * as statvisor from "@statvisor/sdk";

// Basic log
statvisor.log("info", "User signed up", { plan: "pro", userId });

// Warning with context
statvisor.log("warn", "Payment retry triggered", { userId, attempt: 3 });

// Error with details
statvisor.log("error", "Webhook verification failed", { raw: body });
2

Log levels

LevelWhen to use
infoNormal application events — signups, purchases, background jobs completing.
warnDegraded state — retries, slow operations, expected failures you want to track.
errorUnexpected failures that require investigation — exceptions, integration errors.

Request logs (auto-captured by the middleware) are assigned a level automatically: 5xx → error, 4xx → warn, 2xx/3xx → info.

Logging

Viewing and filtering logs

Open any project and click the Logs tab to see all request and custom event logs for that project.

Level filter
Click All, Info, Warn, or Error to narrow the log stream to a specific severity.
Search bar
Type to filter logs by route path (for request logs) or message text (for custom events). Results update as you type.
Near real-time
The dashboard polls every 10 seconds and prepends new entries to the top — no manual refresh needed.
Live mode
Toggle the Live button to switch to 2-second polling for a real-time stream during incidents or deploys.
Expandable rows
Click any log row to expand the full JSON payload — timestamp, level, route, status code, duration, and any custom data you passed.
Load more
Scroll to the bottom and click Load more to fetch older logs. Filters and search are preserved.

Configuration reference

All options are the same across Express, Fastify, Hono, and Next.js integrations.

OptionTypeDefaultDescription
apiKeystringRequired. Your project API key (starts with vl_).
flushIntervalnumber5000How often (ms) the SDK flushes buffered events.
batchSizenumber50Max events to include in a single flush.
ignoreRoutesstring[][]Route paths to exclude from tracking (e.g. ["/health"]).
environmentstringNODE_ENVEnvironment tag attached to events (e.g. "production", "staging").
debugbooleanfalseLog SDK activity to console for troubleshooting.
ingestUrlstringstatvisor.com/…Override the ingest endpoint. Only needed for self-hosted deployments.

Example with all options

app.js
import { express as statvisor } from "@statvisor/sdk";

app.use(statvisor({
  apiKey: process.env.STATVISOR_API_KEY,
  flushInterval: 3000,
  batchSize: 50,
  ignoreRoutes: ["/health", "/ping", "/_internal"],
  environment: process.env.NODE_ENV,
  debug: false,
}));

Using the dashboard

Once your SDK is sending data, the dashboard shows it in real time.

Projects list

The projects page shows all your monitored APIs. Each card displays the project name, domain, and a quick summary. Click a project to open its analytics dashboard. Use the copy button on a project card to copy the API key to your clipboard.

Analytics dashboard

Summary cards
The four cards at the top show total requests, error rate, median latency, and active route count for the selected time range.
Time range selector
Switch between 1h, 6h, 24h, and 7d. All charts and the route table update to match.
Volume chart
Bar chart showing request count and error count over time. Hover a bar to see the exact counts for that interval.
Latency chart
Line chart showing P50, P95, and P99 latency over time.
Route table
Sortable table of every route. Columns: route, method, requests, avg latency, P50/P95/P99, error rate. Click any column header to sort.
Error rate vs health error rate
Error rate counts all 4xx and 5xx. Health error rate excludes your configured 'expected' codes (401, 403, 404 etc.) — it's the signal you actually care about.

Ready to start monitoring?

Free forever. No credit card required.

Create your free account