Skip to content
trakoo
Esc
navigateopen⌘Jpreview
On this page

Custom Providers

Build a provider for any analytics service with a JavaScript SDK.

trakoo ships providers for several services, but you are not limited to them. If a service has a JavaScript SDK or an HTTP API, you can write a provider that sends trakoo events to it. That covers tools like Google Analytics, Mixpanel, Amplitude, Segment, and Customer.io.

A provider is an adapter: it implements a small interface, and the analytics instance drives it. You have two ways to build one — implement the AnalyticsProvider interface directly, or extend BaseAnalyticsProvider to inherit debug logging and enabled-state handling.

The provider interface

Every provider implements the same shape, so the analytics instance can call it 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;
}

A BaseEvent has the shape { category, action, properties }, so event.action and event.properties are what you forward to your service.

Extend BaseAnalyticsProvider

Extending BaseAnalyticsProvider gives you isEnabled() and log() for free, so each method stays focused on the transport.

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

export class CustomProvider extends BaseAnalyticsProvider {
  name = 'CustomProvider';
  private client?: YourSdk;

  constructor(private config: { apiKey: string; debug?: boolean }) {
    super({ debug: config.debug });
  }

  async initialize(): Promise<void> {
    if (!this.isEnabled()) return;
    // Load or configure your SDK here.
    this.log('Initialized');
  }

  track(event: BaseEvent, context?: EventContext): void {
    if (!this.isEnabled() || !this.client) return;
    this.client.track(event.action, event.properties);
    this.log('Tracked event', { event });
  }

  identify(userId: string, traits?: Record<string, unknown>): void {
    if (!this.isEnabled() || !this.client) return;
    this.client.identify(userId, traits);
  }

  pageView(properties?: Record<string, unknown>, context?: EventContext): void {
    if (!this.isEnabled() || !this.client) return;
    this.client.page(properties);
  }

  reset(): void {
    if (!this.isEnabled() || !this.client) return;
    this.client.reset();
  }
}

Register your provider

A custom provider goes in the providers array like any built-in one, and it works with routing the same way.

import { createClientAnalytics } from 'trakoo/client';
import { CustomProvider } from './custom-provider';

const analytics = createClientAnalytics({
  providers: [
    new CustomProvider({ apiKey: 'your-api-key', debug: true })
  ]
});

Best practices

  • Extend BaseAnalyticsProvider to inherit debug logging and enabled-state handling.
  • Check isEnabled() at the top of each method so a disabled provider stays silent.
  • Catch errors instead of throwing — log the failure so one provider can’t break the others.
  • Type your config and export the interface so callers get autocomplete.

Resources

Was this page helpful?