Swetrix
Integrations

Express.js

Integrate Swetrix with your Express.js application using the @swetrix/node server-side SDK to track page views, monitor errors, and capture custom events — all while staying privacy-friendly and GDPR-compliant.

Unlike client-side integrations that inject a tracking script into the browser, @swetrix/node sends analytics data directly from your server. This means tracking works even when visitors have JavaScript disabled or use ad blockers, and no third-party scripts are loaded in the browser.

Installation

npm install @swetrix/node

Setup

Initialise the Swetrix client with your Project ID:

const express = require("express");
const { Swetrix } = require("@swetrix/node");

const app = express();
const swetrix = new Swetrix("YOUR_PROJECT_ID");

Or using ES module syntax:

import express from "express";
import { Swetrix } from "@swetrix/node";

const app = express();
const swetrix = new Swetrix("YOUR_PROJECT_ID");

Replace YOUR_PROJECT_ID with your actual Project ID from the Swetrix dashboard, otherwise tracking won't work.

The Swetrix constructor accepts an optional second argument — an options object:

const swetrix = new Swetrix("YOUR_PROJECT_ID", {
  devMode: false,
  disabled: process.env.NODE_ENV !== "production",
  apiURL: undefined, // only needed for self-hosted Swetrix
});
OptionTypeDescription
devModebooleanWhen true, all tracking logs are printed to the console
disabledbooleanWhen true, no data is sent to the server — useful for development
apiURLstringCustom API server URL (for self-hosted Swetrix)
uniquebooleanWhen true, only unique events are saved
profileIdstringOptional profile ID for long-term user tracking

Tracking pageviews

To track pageviews you need to pass the visitor's IP address and User-Agent header. Without these, unique visitor counting and live visitor tracking won't work. See the Events API docs for details.

Basic example

app.get("/", async (req, res) => {
  const ip = req.ip;
  const userAgent = req.headers["user-agent"];

  await swetrix.trackPageView(ip, userAgent, {
    pg: "/",
    lc: req.headers["accept-language"]?.split(",")[0],
    ref: req.headers["referer"],
  });

  res.send("Hello World");
});

For most applications, you'll want to track pageviews on every request. A middleware makes this clean and automatic:

app.use(async (req, res, next) => {
  const ip = req.ip;
  const userAgent = req.headers["user-agent"];

  // Fire-and-forget — don't block the response
  swetrix.trackPageView(ip, userAgent, {
    pg: req.path,
    lc: req.headers["accept-language"]?.split(",")[0],
    ref: req.headers["referer"],
  });

  next();
});

By not awaiting trackPageView, analytics calls happen in the background without adding latency to your responses. This is the recommended pattern for production.

If your Express app runs behind a reverse proxy (e.g. Nginx, Cloudflare, or a load balancer), make sure to configure the trust proxy setting so req.ip returns the real client IP:

app.set("trust proxy", true);

Pageview options

The third argument to trackPageView is an options object:

OptionTypeDescription
pgstringPage path (e.g. /home)
lcstringVisitor locale (e.g. en-US)
refstringReferrer URL
sostringTraffic source (e.g. utm_source)
mestringTraffic medium (e.g. utm_medium)
castringCampaign (e.g. utm_campaign)
uniquebooleanOnly save unique visits
metaobjectCustom metadata key-value pairs

Tracking custom events

Track specific actions — API calls, form submissions, purchases, etc.:

app.post("/api/subscribe", async (req, res) => {
  const ip = req.ip;
  const userAgent = req.headers["user-agent"];

  swetrix.track(ip, userAgent, {
    ev: "NEWSLETTER_SUBSCRIBE",
    page: "/subscribe",
    meta: {
      plan: req.body.plan,
    },
  });

  res.json({ success: true });
});

Event naming rules

Event names must:

  • Contain any characters (including spaces, unicode, etc.)
  • Be no longer than 256 characters

We recommend UPPER_SNAKE_CASE for consistency (e.g. NEWSLETTER_SUBSCRIBE, CHECKOUT_COMPLETED).

Error tracking

Report server-side errors to Swetrix for centralised monitoring:

app.use((err, req, res, next) => {
  const ip = req.ip;
  const userAgent = req.headers["user-agent"];

  swetrix.trackError(ip, userAgent, {
    name: err.name || "Error",
    message: err.message,
    stackTrace: err.stack,
    pg: req.path,
  });

  res.status(500).json({ error: "Internal Server Error" });
});

You can also track errors from specific routes:

app.get("/api/data", async (req, res) => {
  try {
    const data = await fetchExternalData();
    res.json(data);
  } catch (err) {
    swetrix.trackError(req.ip, req.headers["user-agent"], {
      name: err.name,
      message: err.message,
      stackTrace: err.stack,
      pg: req.path,
    });

    res.status(500).json({ error: "Failed to fetch data" });
  }
});

Heartbeat events

Heartbeat events let Swetrix know a visitor's session is still active, powering the Live Visitors counter in your dashboard. They're most useful in long-lived connections like WebSocket sessions or polling endpoints:

// Example: send heartbeats for active WebSocket connections
const HEARTBEAT_INTERVAL = 30_000;

wss.on("connection", (ws, req) => {
  const ip = req.socket.remoteAddress;
  const userAgent = req.headers["user-agent"];

  const interval = setInterval(() => {
    swetrix.heartbeat(ip, userAgent);
  }, HEARTBEAT_INTERVAL);

  ws.on("close", () => clearInterval(interval));
});

Feature flags

Evaluate feature flags server-side based on visitor context:

app.get("/dashboard", async (req, res) => {
  const ip = req.ip;
  const userAgent = req.headers["user-agent"];

  const showNewDashboard = await swetrix.getFeatureFlag(
    "new-dashboard",
    ip,
    userAgent,
    { profileId: req.user?.id },
    false, // default value
  );

  if (showNewDashboard) {
    res.render("dashboard-v2");
  } else {
    res.render("dashboard");
  }
});

You can also fetch all flags at once:

const flags = await swetrix.getFeatureFlags(ip, userAgent);

if (flags["beta-feature"]) {
  // Enable beta feature
}

Feature flags are cached for 5 minutes. To force a refresh:

swetrix.clearFeatureFlagsCache();

A/B testing (experiments)

Run server-side A/B tests and get the assigned variant for each visitor:

app.get("/pricing", async (req, res) => {
  const ip = req.ip;
  const userAgent = req.headers["user-agent"];

  const variant = await swetrix.getExperiment(
    "pricing-experiment-id",
    ip,
    userAgent,
    { profileId: req.user?.id },
    null, // default variant
  );

  if (variant === "annual-first") {
    res.render("pricing", { showAnnualFirst: true });
  } else {
    res.render("pricing", { showAnnualFirst: false });
  }
});

Revenue attribution

Use profile and session IDs to connect analytics with your payment provider (e.g. Paddle, Stripe):

app.get("/checkout", async (req, res) => {
  const ip = req.ip;
  const userAgent = req.headers["user-agent"];

  const [profileId, sessionId] = await Promise.all([
    swetrix.getProfileId(ip, userAgent),
    swetrix.getSessionId(ip, userAgent),
  ]);

  res.json({
    checkoutConfig: {
      customData: {
        swetrix_profile_id: profileId,
        swetrix_session_id: sessionId,
      },
    },
  });
});

Disable tracking in development

Use the disabled option to prevent tracking during development:

const swetrix = new Swetrix("YOUR_PROJECT_ID", {
  disabled: process.env.NODE_ENV !== "production",
});

Or use devMode to log tracking calls to the console without disabling them:

const swetrix = new Swetrix("YOUR_PROJECT_ID", {
  devMode: process.env.NODE_ENV === "development",
});

Using environment variables for your Project ID

Rather than hardcoding the Project ID, store it in an environment variable:

SWETRIX_PROJECT_ID=YOUR_PROJECT_ID
const swetrix = new Swetrix(process.env.SWETRIX_PROJECT_ID);

If you use dotenv:

npm install dotenv
require("dotenv").config();

const swetrix = new Swetrix(process.env.SWETRIX_PROJECT_ID);

Complete example

Here's a full Express application with Swetrix analytics:

const express = require("express");
const { Swetrix } = require("@swetrix/node");

const app = express();
const swetrix = new Swetrix(process.env.SWETRIX_PROJECT_ID, {
  disabled: process.env.NODE_ENV !== "production",
});

app.set("trust proxy", true);
app.use(express.json());

// Track pageviews on all requests
app.use((req, res, next) => {
  swetrix.trackPageView(req.ip, req.headers["user-agent"], {
    pg: req.path,
    lc: req.headers["accept-language"]?.split(",")[0],
    ref: req.headers["referer"],
  });

  next();
});

app.get("/", (req, res) => {
  res.send("Hello World");
});

app.post("/api/contact", async (req, res) => {
  swetrix.track(req.ip, req.headers["user-agent"], {
    ev: "CONTACT_FORM_SUBMITTED",
    page: "/contact",
  });

  res.json({ success: true });
});

// Error tracking
app.use((err, req, res, next) => {
  swetrix.trackError(req.ip, req.headers["user-agent"], {
    name: err.name || "Error",
    message: err.message,
    stackTrace: err.stack,
    pg: req.path,
  });

  res.status(500).json({ error: "Internal Server Error" });
});

app.listen(3000, () => {
  console.log("Server running on port 3000");
});

Check your installation

Deploy your application (or temporarily enable devMode) and make a few requests. Within a minute you should see new pageviews appearing in your Swetrix dashboard.

Self-hosted Swetrix

If you're self-hosting the Swetrix API, point the apiURL option to your instance:

const swetrix = new Swetrix("YOUR_PROJECT_ID", {
  apiURL: "https://your-swetrix-instance.com/log",
});

Further reading

On this page