pieowl

How to add analytics to Express

Add server-side analytics to your Express app with the Pieowl SDK. Track API requests, user actions and custom events.

Pieowl TeamBeginner · 8 min · 8 Oct 2026
Guide · Express

Server-side tracking gives you event tracking that ad blockers and browser extensions can't block. A few lines of middleware add the Pieowl JavaScript SDK to every request. That makes it easy to track events anywhere in your app.

Pieowl gives you product analytics, such as funnels, retention and user profiles, without cookies. Your data is stored in the EU.

Prerequisites

Server-side tracking needs a clientSecret. A browser sends an origin that Pieowl can check, but a server does not, so the secret proves the request is yours.

Install the SDK

Server-side events use the Pieowl JavaScript SDK. Install it with npm (pnpm and yarn work too).

npm install @pieowl/sdk

Add the middleware

Write a small middleware that creates a Pieowl instance for each request and adds it as req.pieowl. Add it early in your middleware chain so all your route handlers can use it.

import express from 'express';
import { Pieowl } from '@pieowl/sdk';

const app = express();

app.use(express.json());

app.use((req, res, next) => {
  const pieowl = new Pieowl({
    clientId: process.env.PIEOWL_CLIENT_ID!,
    clientSecret: process.env.PIEOWL_CLIENT_SECRET!,
  });

  if (req.ip) pieowl.api.addHeader('pieowl-client-ip', req.ip);
  const userAgent = req.headers['user-agent'];
  if (userAgent) pieowl.api.addHeader('user-agent', userAgent);

  req.pieowl = pieowl;
  next();
});

app.listen(3000, () => {
  console.log('Server running on http://localhost:3000');
});

Keep your credentials in environment variables, not in code. That keeps secrets out of version control and lets you use different keys in development and production.

The middleware forwards the visitor's IP address and user agent from each request. So the country and device data stay correct, even though the events come from your server. The IP is used for the country lookup and is not stored. Behind a proxy or load balancer, set app.set('trust proxy', true) so req.ip is the visitor, not the proxy.

Pages and AI crawlers

Does your Express app serve web pages? Add the HTML script to them for page views and browser events.

AI crawlers and search bots don't run JavaScript, so the script never sees them. To count them, add @pieowl/bots.

npm install @pieowl/bots
import { trackBots } from '@pieowl/bots/express';

app.use(trackBots({
  clientId: process.env.PIEOWL_CLIENT_ID,
  clientSecret: process.env.PIEOWL_CLIENT_SECRET,
}));

Track events

With the middleware in place, call req.pieowl.track() in any route handler. The first argument is the event name. The second is an object of properties.

app.post('/signup', async (req, res) => {
  const { name } = req.body;

  req.pieowl.track('user_signed_up', {
    name,
    source: 'website',
  });

  const user = await createUser(req.body);
  res.json({ success: true, user });
});

Track any event that matters to your business. Common examples are form submits, purchases, feature use and API errors.

app.post('/contact', async (req, res) => {
  const { email, message } = req.body;

  req.pieowl.track('contact_form_submitted', {
    message_length: message.length,
  });

  await sendContactEmail(email, message);
  res.json({ success: true });
});

Automatic request tracking

Your middleware can track requests for you. This is handy for watching API use without adding a tracking call to every route. Add this after the middleware above:

app.use((req, res, next) => {
  if (req.url.startsWith('/api/')) {
    req.pieowl.track('request', {
      url: req.url,
      method: req.method,
      query: req.query,
    });
  }
  next();
});

This sends a request event with the URL, method and query parameters for every API call.

Identify users

To link the automatic request events to a user, pass profileId with the event.

req.pieowl.track('request', {
  url: req.url,
  method: req.method,
  query: req.query,
  profileId: req.user?.id,
});

For your own events, call req.pieowl.identify(). It saves the user's name, email and custom properties to their profile. Each request gets a fresh instance, so events you track after identify in the same handler are linked to that user.

app.post('/login', async (req, res) => {
  const { email, password } = req.body;
  const user = await authenticateUser(email, password);

  req.pieowl.identify({
    profileId: user.id,
    firstName: user.firstName,
    lastName: user.lastName,
    email: user.email,
    properties: {
      plan: user.plan,
      signupDate: user.createdAt,
    },
  });

  req.pieowl.track('user_logged_in', { method: 'email' });
  res.json({ success: true, user });
});

In other handlers, pass profileId with the event instead:

req.pieowl.track('report_exported', {
  profileId: req.user.id,
  format: 'csv',
});

Increment profile properties

To keep a running count on a profile, such as logins or purchases, use increment. The profile must already exist, for example from an earlier identify call.

req.pieowl.increment({
  profileId: user.id,
  property: 'login_count',
  value: 1,
});

Verify your setup

Start your Express server and call a few of your endpoints. Then open the Pieowl dashboard and check the realtime view.

No events? Check your server logs for error responses from Pieowl. Make sure your clientId and clientSecret are correct, and that the middleware runs before your routes.

TypeScript support

To type req.pieowl, add it to the Request type in a declaration file.

import type { Pieowl } from '@pieowl/sdk';

declare global {
  namespace Express {
    export interface Request {
      pieowl: Pieowl;
    }
  }
}

Next steps

The JavaScript SDK reference lists every option and method. The Express docs cover the HTML script, bot tracking and the ad-blocker proxy. If you use another Node.js framework, the Node.js tracking guide shows how to use the core SDK directly. To compare Pieowl with other tools, see the Mixpanel alternative page.

Why do I need a clientSecret for server-side tracking?

A browser sends an origin that Pieowl checks against your site settings. A server does not. The client secret proves the events come from you and blocks anyone else from sending events to your site.

Will tracking slow down my routes?

No. track() sends the event in the background and returns a promise. You don't need to await it, so your response is not held up.

Does Pieowl help with GDPR?

Pieowl is built for GDPR: no cookies, no stored IPs. With server-side tracking, you also decide exactly which data you send. Your data is stored in the EU. Ask your legal advisor about your own setup.

Your first dashboard in five minutes.

14-day free trial, no card needed. Cloud in the EU. No cookies.