Skip to content

createServerTracker()

The server tracker. Sends GA4 events via the Measurement Protocol and Google Ads conversions via the Ads API. Manages the OAuth refresh-token / access-token cycle internally — you give it a refresh token once, it handles the rest.

Import only from server-side code. The credentials this takes must never reach the browser.

import { createServerTracker } from '@trackbridge/server';
function createServerTracker(config: ServerTrackerConfig): ServerTracker;
type ServerTrackerConfig = {
ga4MeasurementId: string;
ga4ApiSecret: string;
ads?: ServerAdsConfig;
debug?: boolean;
fetch?: typeof globalThis.fetch;
now?: () => number;
generateTransactionId?: () => string;
};
type ServerAdsConfig = {
developerToken: string;
customerId: string;
refreshToken: string;
clientId: string;
clientSecret: string;
loginCustomerId?: string;
conversionActions: Record<string, string>;
/** Default: 'v17'. */
apiVersion?: string;
};
FieldRequiredNotes
ga4MeasurementIdyesThe GA4 property of the form G-XXXXXXXXXX.
ga4ApiSecretyesThe Measurement Protocol API secret minted in GA4 Admin → Data streams.
adsnoRequired to call serverTracker.trackConversion. Omit for GA4-events-only setups.
ads.developerTokenyes (if ads)Google Ads developer token from your manager (MCC) account.
ads.customerIdyes (if ads)Customer ID, digits only1234567890, not 123-456-7890.
ads.refreshTokenyes (if ads)Long-lived OAuth refresh token. Best obtained from a Workspace account, not personal.
ads.clientIdyes (if ads)OAuth client ID from Google Cloud Console.
ads.clientSecretyes (if ads)OAuth client secret.
ads.loginCustomerIdnoManager (MCC) ID, digits only. Only required if your MCC sits between the access-granted account and the customer account.
ads.conversionActionsyes (if ads)Mapping from your friendly labels to Ads API resource names. See Mapping conversion actions.
ads.apiVersionno, default 'v17'The Ads API version segment in the request URL.
debugno, default falseWhen true, non-2xx responses and network errors are logged via console.warn. The auto-transactionId warning fires regardless.
fetchno, default globalThis.fetchTest seam.
nowno, default Date.nowTest seam.
generateTransactionIdnoTest seam. Default: tb_${crypto.randomUUID()}.
type ServerTracker = {
trackEvent(input: ServerEventInput): Promise<void>;
trackConversion(input: ServerConversionInput): Promise<void>;
};

See:

If ads is configured, the constructor wires an OAuth access-token provider (which will refresh on first use, then cache until ~60 seconds before expiry) and an Ads API client. No network requests fire at init — the first trackConversion call triggers the first refresh.

The constructor itself never throws on unreachable APIs or invalid credentials; those failures surface at call time.

Throws synchronously at init if either of these is missing:

Error: [trackbridge] ga4MeasurementId is required
Error: [trackbridge] ga4ApiSecret is required

trackConversion throws at call time if ads is not configured, or if the label you pass isn’t in the conversionActions map. See trackConversion.

lib/tracker.server.ts
import 'server-only';
import { createServerTracker } from '@trackbridge/server';
export const serverTracker = createServerTracker({
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: 'customers/1234567890/conversionActions/9876543210',
signup: 'customers/1234567890/conversionActions/1122334455',
},
},
debug: process.env.NODE_ENV !== 'production',
});

The import 'server-only' line is the cheapest insurance against accidentally importing this from a client component. Next.js will fail the build; other frameworks have equivalent guards.