@addilytics/fastify
v0.0.1
Published
Fastify plugin and bundled browser relay for Addilytics.
Readme
@addilytics/fastify
Server-side analytics for Fastify 4.29+ and 5.
Requires Node.js 20 or newer.
import Fastify from 'fastify';
import { addilytics } from '@addilytics/fastify';
const app = Fastify();
await app.register(
addilytics({
endpoint: 'https://addilytics.example',
siteKey: process.env.ADDILYTICS_KEY!
})
);Register the plugin before routes. Its non-encapsulated hooks then cover the routes declared afterward.
The plugin uses Fastify's public onResponse hook, after every onSend hook and after Fastify sends the response. It reads the final status and headers without touching the payload. Analytics work therefore doesn't add response latency. A normal Node process keeps the outbound request alive, but a short-lived serverless runtime may stop it. If your host provides waitUntil, pass it in so the runtime keeps the task alive:
await app.register(
addilytics({
endpoint: 'https://addilytics.example',
siteKey: process.env.ADDILYTICS_KEY!,
waitUntil: (request) => {
const context = getExecutionContext(request);
return context.waitUntil.bind(context);
}
})
);Fastify's onResponse hook doesn't run for responses sent with reply.hijack(), so the plugin doesn't count those responses.
Bundled navigation tracking
The default server mode counts document requests handled by Fastify. 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:
await app.register(
addilytics({
endpoint: 'https://addilytics.example',
mode: 'client',
siteKey: process.env.ADDILYTICS_KEY!
})
);import { createBrowserTracker } from '@addilytics/fastify/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 code is part of your application bundle. It posts to the plugin's same-origin
/__addilytics route and contains no site key. Addilytics does not ship a hosted script. Use
hybrid on both sides when Fastify always sees the initial document. Hybrid keeps the server
pageview and skips the browser's first callback. If you change the browser endpoint, set the same
path as relayPath on the plugin.
Behind a TLS-terminating proxy, configure Fastify's trustProxy server option 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
Every request gets a typed request.addilytics helper:
app.post('/checkout', async (request, reply) => {
const order = await createOrder(request.body);
await request.addilytics.track('purchase', {
props: { currency: 'USD', total: order.total }
});
return reply.code(201).send(order);
});The user and ip options receive the Fastify request and reply. Explicit userId values passed to track take priority over user.
