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
BaseAnalyticsProviderto 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.
