Skip to content

defineServerTracker()

Wraps createServerTracker in a lazy module-level singleton suitable for Next.js route handlers and Server Actions. Avoids re-constructing the tracker (and re-reading env vars) on every request.

import { defineServerTracker } from '@trackbridge/sdk/next/server';
function defineServerTracker(configFn: () => ServerTrackerConfig): ServerTrackerFactory;
type ServerTrackerFactory = () => ServerTracker;

The returned factory is the entry point. On first call:

  1. Invokes configFn().
  2. Passes the result to createServerTracker.
  3. Caches the resulting ServerTracker and returns it.

On subsequent calls, returns the cached instance without re-invoking configFn.

If configFn (or createServerTracker) throws, the error is cached and re-thrown on every subsequent call without re-invoking the factory. This prevents a misconfigured tracker from blasting the same env-var resolution on every request, and surfaces the original error verbatim. To recover, fix the config and reload the module.

createServerTracker itself is cheap — no network calls, just builds an OAuth token provider and caches it in closure. But re-constructing it per request would mean:

  • Re-reading process.env on every request.
  • Re-instantiating the OAuth refresh-token cache (so every request would refresh anew).
  • Re-applying any future cold-start initialization.

The singleton avoids all of that with one line of code, and matches the standard Next.js pattern for module-level resources (Prisma, Redis, etc.).

lib/tracker.server.ts
import 'server-only';
import { defineServerTracker } from '@trackbridge/sdk/next/server';
export const getServerTracker = defineServerTracker(() => ({
ga4MeasurementId: process.env.NEXT_PUBLIC_GA4_MEASUREMENT_ID!,
ga4ApiSecret: process.env.GA4_API_SECRET!,
ads: {
developerToken: process.env.GOOGLE_ADS_DEVELOPER_TOKEN!,
customerId: process.env.GOOGLE_ADS_CUSTOMER_ID!,
refreshToken: process.env.GOOGLE_ADS_REFRESH_TOKEN!,
clientId: process.env.GOOGLE_OAUTH_CLIENT_ID!,
clientSecret: process.env.GOOGLE_OAUTH_CLIENT_SECRET!,
conversionActions: {
purchase: process.env.GOOGLE_ADS_PURCHASE_RESOURCE_NAME!,
},
},
conversionLabels: {
purchase: process.env.NEXT_PUBLIC_GOOGLE_ADS_PURCHASE_LABEL,
},
}));

Use it from a route handler:

app/api/webhooks/stripe/route.ts
import { getServerTracker } from '@/lib/tracker.server';
export async function POST(req: Request) {
const event = await verifyStripeSignature(req);
if (event.type !== 'checkout.session.completed') return new Response();
const order = await db.orders.findByStripeId(event.data.object.id);
const serverTracker = getServerTracker();
const result = await serverTracker.trackPurchase({
transactionId: order.id,
value: order.total,
currency: order.currency,
items: order.items,
clientId: order.gaClientId,
gclid: order.gclid,
userData: { email: order.customer.email },
});
if (!result.ads.ok && !('skipped' in result.ads)) reportError(result.ads.error);
return new Response();
}