A server-side analytics component for tracking user events with PostHog in your Convex application.
- Server-side tracking: All events are tracked from your backend, ensuring accurate and secure analytics
- Non-blocking: Events are scheduled as background jobs, so they never slow down your mutations
- Type-safe: Full TypeScript support with proper types
- Simple API: Easy-to-use interface for tracking events
- Flexible configuration: Support for both environment variables and explicit configuration
npm install @samhoque/convex-posthogCreate or update convex/convex.config.ts:
import { defineApp } from "convex/server";
import posthog from "@samhoque/convex-posthog/convex.config";
const app = defineApp();
app.use(posthog);
export default app;Set these environment variables in your Convex deployment:
POSTHOG_API_KEY=your_api_key_here
POSTHOG_HOST=https://us.i.posthog.com # Optional, defaults to US Cloud
# For EU Cloud use: https://eu.i.posthog.comYou can set these in the Convex dashboard or via the CLI:
npx convex env set POSTHOG_API_KEY your_api_key_here
npx convex env set POSTHOG_HOST https://us.i.posthog.com # Or https://eu.i.posthog.com for EU Cloudimport { mutation } from "./_generated/server";
import { components } from "./_generated/api";
import { PostHog } from "@samhoque/convex-posthog";
import { v } from "convex/values";
// Initialize the PostHog component
const posthog = new PostHog(components.posthog, {
// API key and host are read from environment variables by default
// Or you can pass them explicitly:
// apiKey: process.env.POSTHOG_API_KEY,
// host: process.env.POSTHOG_HOST,
});
export const signupUser = mutation({
args: {
userId: v.string(),
email: v.string(),
name: v.string(),
},
handler: async (ctx, args) => {
// Your business logic here
// ...
// Track the signup event
await posthog.trackUserEvent(ctx, {
userId: args.userId,
event: "user_signed_up",
properties: {
email: args.email,
name: args.name,
signupMethod: "email",
},
});
return { success: true };
},
});export const trackPurchase = mutation({
args: {
userId: v.string(),
productId: v.string(),
amount: v.number(),
},
handler: async (ctx, args) => {
// Your business logic
// ...
// Track the purchase event
await posthog.trackUserEvent(ctx, {
userId: args.userId,
event: "product_purchased",
properties: {
productId: args.productId,
amount: args.amount,
currency: "USD",
},
});
return { success: true };
},
});You can configure the PostHog component when instantiating it:
const posthog = new PostHog(components.posthog, {
apiKey: "your_api_key", // Defaults to process.env.POSTHOG_API_KEY
host: "https://us.i.posthog.com", // Defaults to process.env.POSTHOG_HOST or US Cloud
// For EU Cloud use: "https://eu.i.posthog.com"
// For self-hosted use your domain
});new PostHog(component: PostHogComponent, options?: PostHogOptions)Options:
apiKey(string, optional): PostHog API key. Defaults toPOSTHOG_API_KEYenv varhost(string, optional): PostHog host URL. Defaults toPOSTHOG_HOSTenv var orhttps://us.i.posthog.com(US Cloud). Usehttps://eu.i.posthog.comfor EU Cloud or your self-hosted domain.
Track a user event asynchronously.
await posthog.trackUserEvent(ctx, {
userId: string, // Distinct ID for the user
event: string, // Event name
properties?: object // Optional event properties
});Parameters:
ctx: Mutation context (must haveschedulercapability)data.userId: The unique identifier for the userdata.event: Name of the event to trackdata.properties: Optional object with event properties
Note: This method schedules the tracking as a background job using ctx.scheduler.runAfter(0, ...), which means:
- It won't block your mutation
- Analytics failures won't affect your app's functionality
- Events are tracked asynchronously
- When you call
trackUserEvent, it schedules a background action using Convex's scheduler - The background action uses the PostHog HTTP API directly (no Node.js dependencies required)
- Events are sent to PostHog with automatic metadata (timestamp, lib version, etc.)
- If tracking fails, it fails silently without affecting your app
To develop this component:
# Install dependencies
npm install
# Run the example app (generates types and starts dev server)
npm run dev
# Build the component
npm run build
# Run tests
npm test
# Type check
npm run typecheckApache-2.0