Skip to content
trakoo
Esc
navigateopen⌘Jpreview
On this page

How Providers Work

The plugin architecture that delivers your events — fan-out to many services, a shared lifecycle, and graceful failure.

A provider is a small adapter that translates a trakoo event into the API of a specific analytics service. You call track(); the provider handles the transport.

const analytics = createClientAnalytics({
  providers: [new PostHogClientProvider({ token: 'xxx' })]
});

analytics.track('user_signed_up', { email: 'ada@example.com' });
// PostHogClientProvider transforms the event and sends it to PostHog.

This indirection is what lets your tracking code outlive your tooling. Switch from PostHog to Pirsch, or add a second destination, and your track() calls don’t change — only the providers array does.

One instance, many providers

An analytics instance can hold any number of providers. Each call fans out to all of them.

const analytics = createClientAnalytics({
  providers: [
    new PostHogClientProvider({ token: 'xxx' }),     // product analytics
    new PirschClientProvider({ identificationCode: 'yyy' }), // privacy-friendly
    new BentoClientProvider({ siteUuid: 'zzz' })     // email automation
  ]
});

// All three providers receive this event.
analytics.track('user_signed_up', { email: 'ada@example.com', plan: 'pro' });

Not every provider should receive every call — Bento is built for identified users, not anonymous page views. Control which methods and events reach each provider with routing.

The provider lifecycle

Every provider implements the same interface, so the analytics instance can drive them uniformly:

interface AnalyticsProvider {
  name: string;
  initialize(): Promise<void> | void;
  identify(userId: string, traits?: Record<string, unknown>): Promise<void> | void;
  track(event: BaseEvent, context?: EventContext): Promise<void> | void;
  pageView(properties?: Record<string, unknown>, context?: EventContext): Promise<void> | void;
  pageLeave?(properties?: Record<string, unknown>, context?: EventContext): Promise<void> | void;
  reset(): Promise<void> | void;
  flush?(useBeacon?: boolean): Promise<void> | void;
}
  • initialize runs when the instance is created — loading a script, opening a connection, or setting up the SDK. It’s always called, regardless of routing.
  • track, identify, pageView, pageLeave, reset map to the calls you make on the analytics instance.
  • Server providers may also expose shutdown(), which ServerAnalytics.shutdown() calls to flush queued events before the process exits.

You rarely call these directly — the instance does. See Client vs Server for when each one fires.

Resilient by default

Providers are isolated from one another. If one fails, the others still receive the event, and the error is caught rather than thrown into your app.

analytics.track('purchase', { amount: 99.99 });
// If PostHog is unreachable, Bento and Pirsch still get the event.

Turn on debug to surface what would otherwise be swallowed:

const analytics = createClientAnalytics({
  providers: [new PostHogClientProvider({ token: 'xxx', debug: true })],
  debug: true
});

Import from the right entry point

Providers are split by environment. Import browser providers from trakoo/providers/client and server providers from trakoo/providers/server so nothing server-only ends up in your browser bundle.

// Browser
import { PostHogClientProvider } from 'trakoo/providers/client';

// Server
import { PostHogServerProvider } from 'trakoo/providers/server';

Next steps

Was this page helpful?