Skip to content

Platforms

handle(request) takes a standard Request and resolves to a Response for tracker requests under /_i/ (change it with prefix), or null for everything else so your app answers. Pass waitUntil where the platform has it, so recording finishes after the response.

import { createClient } from '@plaintrace/server'
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
const analytics = createClient({ key: env.ANALYTICS_KEY, endpoint: 'https://collect.plaintrace.dev' })
const res = await analytics.handle(request, { waitUntil: (p) => ctx.waitUntil(p) })
if (res) return res
return fetch(request) // or your app
},
}

CF-Connecting-IP is trusted by default on Workers. Creating the client per request is fine; create it once at module level if you prefer.

See the Next.js guide.

import { createClient } from '@plaintrace/server'
const analytics = createClient({ key: Bun.env.ANALYTICS_KEY!, endpoint: 'https://collect.plaintrace.dev' })
Bun.serve({
async fetch(request, server) {
// Exposed directly: the socket address is the visitor's.
const res = await analytics.handle(request, { ip: server.requestIP(request)?.address })
return res ?? app(request)
},
})

Behind a reverse proxy, drop ip and pass ipHeader (for example 'x-real-ip') to createClient instead: ip always wins, and the socket address would then be the proxy’s.

import { createClient } from 'npm:@plaintrace/server'
const analytics = createClient({ key: Deno.env.get('ANALYTICS_KEY')!, endpoint: 'https://collect.plaintrace.dev' })
// Exposed directly; behind a reverse proxy use ipHeader on createClient instead of ip.
Deno.serve(async (request, info) => (await analytics.handle(request, { ip: info.remoteAddr.hostname })) ?? app(request))

Node 24 frameworks built on web Request (Hono, for example) work as above. With a bare Node server, put a proxy that sets x-real-ip in front and pass ipHeader: 'x-real-ip'; behind no proxy, pass ip yourself.

Point the tracker at your own domain once the proxy runs:

<script async src="/_i/t.js" data-site="<site id>" data-endpoint="/_i"></script>