Skip to content
trakoo
Esc
↑↓navigate↵open⌘Jpreview
On this page

Providers

Choose official providers, understand support by environment, and route events intentionally.

Providers translate trakoo events into the APIs of the services you already use. You can start with one provider, add another later, or route specific methods and event names to the tools that should receive them.

Support matrix

Provider Client import Server import Package Best for
PostHog PostHogClientProvider PostHogServerProvider @trakoo/posthog Product analytics across browser and backend
OpenPanel OpenPanelClientProvider OpenPanelServerProvider @trakoo/openpanel Open-source web and product analytics
Bento BentoClientProvider BentoServerProvider Client: trakoo; server: @trakoo/bento Identified lifecycle events and email automation
Pirsch PirschClientProvider PirschServerProvider trakoo Privacy-friendly page views and lightweight events
EmitKit — EmitKitServerProvider @trakoo/emitkit Notifications and activity channels
Visitors VisitorsClientProvider — trakoo Lightweight web analytics and revenue attribution
Proxy ProxyProvider ingestProxyEvents, createProxyHandler trakoo Sending browser events through your own API

Providers included in trakoo import from trakoo/providers/client and trakoo/providers/server. Providers that wrap a vendor SDK ship as their own package; import them from its /client or /server subpath, such as @trakoo/posthog/client, and install only the SDK for that side. See Installation.

One event, multiple destinations

import { createClientAnalytics } from 'trakoo/client';
import {
  BentoClientProvider,
  VisitorsClientProvider
} from 'trakoo/providers/client';
import { PostHogClientProvider } from '@trakoo/posthog/client';
import { appEvents } from '@/lib/events';

const analytics = createClientAnalytics({
  events: appEvents,
  providers: [
    new PostHogClientProvider({
      token: import.meta.env.VITE_POSTHOG_KEY
    }),
    {
      provider: new BentoClientProvider({
        siteUuid: import.meta.env.VITE_BENTO_SITE_UUID
      }),
      methods: ['identify', 'track']
    },
    new VisitorsClientProvider({
      token: import.meta.env.VITE_VISITORS_TOKEN,
      persist: true
    })
  ]
});

await analytics.track('user_signed_up', {
  email: 'ada@example.com',
  plan: 'pro'
});

The application emits one typed event. trakoo fans it out to each enabled provider.

Routing

Some providers should not receive every call. Bento is usually better for identified user lifecycle events than anonymous page views. EmitKit is usually better for selected events than high-volume click streams. Wrap a provider in a config object to control exactly which methods and events reach it.

import { createClientAnalytics } from 'trakoo/client';
import { BentoClientProvider } from 'trakoo/providers/client';
import { PostHogClientProvider } from '@trakoo/posthog/client';
import { appEvents } from '@/lib/events';

const analytics = createClientAnalytics({
  events: appEvents,
  providers: [
    new PostHogClientProvider({ token: import.meta.env.VITE_POSTHOG_KEY }),
    {
      provider: new BentoClientProvider({
        siteUuid: import.meta.env.VITE_BENTO_SITE_UUID
      }),
      methods: ['identify', 'track'],
      eventPatterns: ['user_*', 'checkout_*']
    }
  ]
});

Routing options are part of the shared provider config:

Option Effect
methods Only call these provider methods (initialize, identify, track, pageView, pageLeave, reset, and on the server group and revenue)
exclude Call every method except these
events Only track these exact event names
excludeEvents Track every event except these names
eventPatterns Track events matching glob-style patterns such as newsletter_*
pii false keeps personal data from this provider (default true)

methods wins over exclude when both are set. events, eventPatterns, and excludeEvents are mutually exclusive; the runtime logs a warning and uses the highest-priority option.

Personal data

An email tool needs the user’s email; a product-analytics tool usually shouldn’t store it. Set pii: false on a provider entry to keep personal data from that provider while the others still receive it:

providers: [
  { provider: new OpenPanelServerProvider({ /* ... */ }), pii: false },
  new BentoServerProvider({ /* ... */ }),
]

A provider with pii: false never receives the email, name, firstName, lastName, or phone traits in identify(), nor the email or those traits in an event’s user context. It still receives the user id, event properties, and the rest of the context. Keep personal data out of event properties themselves: pii doesn’t inspect them.

Browser-to-server delivery

Use the proxy when browser events should go through your domain before reaching server providers.

import { createClientAnalytics } from 'trakoo/client';
import { ProxyProvider } from 'trakoo/providers/client';
import { appEvents } from '@/lib/events';

export const analytics = createClientAnalytics({
  events: appEvents,
  providers: [
    new ProxyProvider({
      endpoint: '/api/events',
      batch: { size: 10, interval: 2000 }
    })
  ]
});
import { createServerAnalytics } from 'trakoo/server';
import { createProxyHandler } from 'trakoo/providers/server';
import { EmitKitServerProvider } from '@trakoo/emitkit/server';
import { appEvents } from '@/lib/events';

const serverAnalytics = createServerAnalytics({
  events: appEvents,
  providers: [
    new EmitKitServerProvider({
      apiKey: process.env.EMITKIT_API_KEY!,
      channelName: 'product-events'
    })
  ]
});

export const POST = createProxyHandler(serverAnalytics);

Build your own provider

If a service is not built in, implement the AnalyticsProvider interface or extend BaseAnalyticsProvider.

import {
  BaseAnalyticsProvider,
  type BaseEvent,
  type EventContext
} from 'trakoo/client';

export class CustomProvider extends BaseAnalyticsProvider {
  name = 'CustomProvider';

  initialize() {}

  track(event: BaseEvent, context?: EventContext) {
    console.log(event.action, event.properties, context);
  }

  identify(userId: string, traits?: Record<string, unknown>) {}

  pageView(properties?: Record<string, unknown>, context?: EventContext) {}

  reset() {}
}

Learn more about custom providers →

Was this page helpful?