Server-Side Analytics
Learn how to track analytics events from API routes, server actions, and other server-side code in your Makerkit application.
Makerkit provides a separate analytics export for server-side tracking. This allows you to track events from API routes, server actions, and other backend code where the client-side analytics SDK isn't available.
Server-Side Analytics
Track events from your backend code
Why Server-Side Analytics
Server-side analytics is useful when:
- Tracking sensitive events - Some events shouldn't be exposed to the client (revenue, internal metrics)
- Ensuring reliability - Server events can't be blocked by ad blockers
- Backend operations - Track events from webhooks, cron jobs, or background tasks
- Data integrity - Server-side tracking is harder to spoof
Using Server Analytics
Import from @kit/analytics/server instead of @kit/analytics:
app/api/webhook/route.ts
import { analytics } from '@kit/analytics/server';export async function POST(request: Request) { const data = await request.json(); // Track the webhook event await analytics.trackEvent('webhook.received', { type: data.type, source: 'stripe', }); // Process the webhook... return Response.json({ received: true });}The API is identical to client-side analytics:
// Identify a userawait analytics.identify(userId, { plan: 'pro' });// Track an eventawait analytics.trackEvent('subscription.created', { plan: 'pro', amount: '29',});// Track a page view (less common server-side)await analytics.trackPageView('/api/data');Default Server Configuration
By default, server analytics uses the NullAnalyticsService:
packages/analytics/src/server.ts
import 'server-only';import { createAnalyticsManager } from './analytics-manager';import { NullAnalyticsService } from './null-analytics-service';import type { AnalyticsManager } from './types';export const analytics: AnalyticsManager = createAnalyticsManager({ providers: { null: () => NullAnalyticsService, },});This means server-side tracking is disabled until you implement a provider that supports server-side calls.
Implementing a Server Provider
Not all analytics services support server-side tracking. For those that do (like PostHog, Segment, or Mixpanel), create a server-specific provider:
packages/analytics/src/providers/posthog-server-service.ts
import { PostHog } from 'posthog-node';import type { AnalyticsService } from '../types';export class PostHogServerService implements AnalyticsService { private client: PostHog; constructor() { const apiKey = process.env.POSTHOG_API_KEY; if (!apiKey) { throw new Error('POSTHOG_API_KEY is not defined'); } this.client = new PostHog(apiKey, { host: process.env.POSTHOG_HOST || 'https://app.posthog.com', }); } async initialize(): Promise<void> { // PostHog Node client initializes in constructor } async identify( userId: string, traits?: Record<string, string> ): Promise<void> { this.client.identify({ distinctId: userId, properties: traits, }); } async trackPageView(path: string): Promise<void> { // Page views are typically client-side only // You can implement if needed } async trackEvent( eventName: string, eventProperties?: Record<string, string | string[]> ): Promise<void> { this.client.capture({ distinctId: 'server', event: eventName, properties: eventProperties, }); }}Register it in the server analytics configuration:
packages/analytics/src/server.ts
import 'server-only';import { createAnalyticsManager } from './analytics-manager';import { PostHogServerService } from './providers/posthog-server-service';import type { AnalyticsManager } from './types';export const analytics: AnalyticsManager = createAnalyticsManager({ providers: { posthog: () => new PostHogServerService(), },});Example: Tracking in Server Actions
lib/actions/create-project.ts
'use server';import { analytics } from '@kit/analytics/server';export async function createProject(formData: FormData) { const name = formData.get('name') as string; const userId = await getCurrentUserId(); // Create the project... const project = await db.project.create({ data: { name, userId }, }); // Track the event server-side await analytics.trackEvent('project.created', { projectId: project.id, userId, }); return project;}Example: Tracking Webhook Events
app/api/stripe/webhook/route.ts
'use server';import { analytics } from '@kit/analytics/server';export async function POST(request: Request) { const event = await parseStripeWebhook(request); switch (event.type) { case 'checkout.session.completed': await analytics.trackEvent('payment.completed', { amount: String(event.data.object.amount_total), currency: event.data.object.currency, customerId: event.data.object.customer as string, }); break; case 'customer.subscription.deleted': await analytics.trackEvent('subscription.cancelled', { customerId: event.data.object.customer as string, reason: event.data.object.cancellation_details?.reason || 'unknown', }); break; } return Response.json({ received: true });}Server-Only Import Guard
The server analytics module uses the 'server-only' directive:
import 'server-only';This ensures the module can only be imported in server-side code. If you accidentally import it in a client component, Next.js will throw a build error.