npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@addilytics/express

v0.0.1

Published

Express middleware and bundled browser relay for Addilytics.

Readme

@addilytics/express

Server-side analytics middleware for Express 4 and 5.

Requires Node.js 20 or newer.

import express from 'express';
import { createAddilyticsMiddleware } from '@addilytics/express';

const app = express();
const analytics = createAddilyticsMiddleware({
	endpoint: 'https://addilytics.example',
	siteKey: process.env.ADDILYTICS_KEY!,
	user: (_request, response) => response.locals.userId
});

app.use(sessionMiddleware);
app.use(analytics);
app.use(express.json());

Register authentication and session middleware first, Addilytics second, and body parsers, routes, error handlers, and response-mutating middleware after it. Addilytics answers relay requests without calling later middleware, so its user resolver can read response.locals only when an earlier middleware populated it. A resolver that reads the raw request does not need that setup. Express skips middleware that appears after a route ends the response. Addilytics listens for the response's finish event, then reads the final status and content type without touching the body. Aborted responses don't produce pageviews.

Express has no request-lifetime API that can await work started after finish. A normal Node server will usually keep the outbound request alive, but short-lived serverless runtimes may stop it. If your host exposes waitUntil, pass it in:

const analytics = createAddilyticsMiddleware({
	endpoint: 'https://addilytics.example',
	siteKey: process.env.ADDILYTICS_KEY!,
	waitUntil: (_request, response) => {
		const context = response.locals.executionContext;
		return context?.waitUntil.bind(context);
	}
});

Bundled navigation tracking

The default server mode counts document requests handled by Express. To cover prerendered pages, CDN hits, and client-side navigation, set mode: 'client' and call the tracker from your browser router's committed-navigation hook:

const analytics = createAddilyticsMiddleware({
	endpoint: 'https://addilytics.example',
	mode: 'client',
	siteKey: process.env.ADDILYTICS_KEY!
});

app.use(sessionMiddleware);
app.use(analytics);
app.use(express.json());
import { createBrowserTracker } from '@addilytics/express/browser';

const tracker = createBrowserTracker({ mode: 'client' });
void tracker.pageview();
export const trackCommittedNavigation = (url: URL, key: string) =>
	tracker.pageview(url, { navigationKey: key });
window.addEventListener('pageshow', (event) => {
	if (event.persisted) void tracker.pageview(location.href, { force: true });
});

Call trackCommittedNavigation from the router's official post-navigation hook. Do not treat a loader, prefetch, or data request as a navigation.

This is application code compiled by your build, not a hosted script. It posts to the middleware's same-origin /__addilytics relay and contains no site key. Mount authentication before Addilytics, then mount body parsers after it so the relay can read its small request body. Use hybrid on both sides when Express always sees the initial document. Hybrid keeps that server pageview and skips the browser's first callback. If you change the browser endpoint, set the same path as relayPath on the middleware.

Behind a TLS-terminating proxy, configure Express's trusted proxy setting so request.protocol reflects the public HTTPS origin. The relay rejects the request when its server-side URL and browser Origin do not match.

Custom events

Use the same middleware instance from a route. track reuses the request metadata captured when the middleware ran and never reads the request body.

app.post('/checkout', async (request, response, next) => {
	try {
		const order = await createOrder(request.body);
		await analytics.track(request, 'purchase', {
			props: { currency: 'USD', total: order.total }
		});
		response.status(201).json(order);
	} catch (error) {
		next(error);
	}
});

The user and ip options receive the Express request and response. Explicit userId values passed to track take priority over user.