JavaScript (Node.js) SDK
Official Klime SDK for JavaScript (Node.js). Track events, identify users, and associate them with groups.
Installation
npm install @klime/nodeQuick Start
// ESM
import { KlimeClient } from "@klime/node";
// CommonJS
// const { KlimeClient } = require("@klime/node");
const client = new KlimeClient({
writeKey: "your-write-key",
});
// Identify a user
client.identify("user_123", {
email: "user@example.com",
name: "Stefan",
});
// Track an event
client.track(
"Button Clicked",
{
buttonName: "Sign up",
plan: "pro",
},
{ userId: "user_123" }
);
// Associate user with a group and set group traits
client.group(
"org_456",
{ name: "Acme Inc", plan: "enterprise" },
{ userId: "user_123" }
);
// Or just link the user to a group (if traits are already set)
client.group("org_456", null, { userId: "user_123" });
// Shutdown gracefully
process.on("SIGTERM", async () => {
await client.shutdown();
process.exit(0);
});Installation Prompt
Copy and paste this prompt into Cursor, Copilot, or your favorite AI editor to integrate Klime:
Integrate Klime for customer analytics. Klime tracks user activity to identify which customers are healthy vs at risk of churning.
ANALYTICS MODES (determine which applies):
- Companies & Teams: Your customers are companies with multiple team members (SaaS, enterprise tools)
→ Use identify() + group() + track()
- Individual Customers: Your customers are individuals with private accounts (consumer apps, creator tools)
→ Use identify() + track() only (no group() needed)
KEY CONCEPTS:
- Every track() call requires either userId OR groupId (no anonymous events)
- Use groupId alone for org-level events (webhooks, cron jobs, system metrics)
- group() links a user to a company AND sets company traits (only for Companies & Teams mode)
- Order doesn't matter - events before identify/group still get attributed correctly
BEST PRACTICES:
- Initialize client ONCE at app startup (singleton pattern)
- Store write key in KLIME_WRITE_KEY environment variable
- Call shutdown() on SIGTERM/SIGINT to flush remaining events
Install: npm install @klime/node
const { KlimeClient } = require("@klime/node");
// Initialize with your KLIME_WRITE_KEY environment variable
const client = new KlimeClient({ writeKey: "YOUR_WRITE_KEY" });
// Identify users at signup/login:
client.identify("usr_abc123", { email: "jane@acme.com", name: "Jane Smith" });
// Track key activities:
client.track("Report Generated", { report_type: "revenue" }, { userId: "usr_abc123" });
client.track("Feature Used", { feature: "export", format: "csv" }, { userId: "usr_abc123" });
client.track("Teammate Invited", { role: "member" }, { userId: "usr_abc123" });
// If Companies & Teams mode: link user to their company and set company traits
client.group("org_456", { name: "Acme Inc", plan: "enterprise" }, { userId: "usr_abc123" });
// Graceful shutdown:
process.on("SIGTERM", async () => { await client.shutdown(); process.exit(0); });
INTEGRATION WORKFLOW:
Phase 1: Discover
Explore the codebase to understand:
1. What framework is used? (Express, Hono, Fastify, Koa, Next.js, NestJS, etc.)
2. Where is user identity available? (e.g., req.user.id, ctx.state.user, session.userId, JWT payload)
3. Is this Companies & Teams or Individual Customers?
- Look for: organization, workspace, tenant, team, account models → Companies & Teams (use group())
- No company/org concept, just individual users → Individual Customers (skip group())
4. Where do core user actions happen? (route handlers, services, controllers)
5. Is there existing analytics? (search: segment, posthog, mixpanel, amplitude, .track)
Match your integration style to the framework's conventions.
Phase 2: Instrument
Add these calls using idiomatic patterns for the framework:
- Initialize client once at startup (Express: app.js, Next.js: instrumentation.ts, NestJS: module provider)
- identify() in auth/login success handler
- group() when user-org association is established (Companies & Teams mode only)
- track() for key user actions (see below)
WHAT TO TRACK:
Active engagement (primary): feature usage, resource creation, collaboration, completing flows
Session signals (secondary): login/session start, dashboard access - distinguishes "low usage" from "churned"
Do NOT track: every page view, every API request, health checks, background jobs
Phase 3: Verify
Confirm: client initialized, shutdown handled, identify/group/track calls added
Phase 4: Summarize
Report what you added:
- Files modified and what was added to each
- Events being tracked (list event names and what triggers them)
- How userId is obtained (and groupId if Companies & Teams mode)
- Any assumptions made or questionsAPI Reference
Constructor
new KlimeClient(config: {
writeKey: string; // Required: Your Klime write key
endpoint?: string; // Optional: API endpoint (default: https://i.klime.com)
flushInterval?: number; // Optional: Milliseconds between flushes (default: 2000)
maxBatchSize?: number; // Optional: Max events per batch (default: 20, max: 100)
maxQueueSize?: number; // Optional: Max queued events (default: 1000)
retryMaxAttempts?: number; // Optional: Max retry attempts (default: 5)
retryInitialDelay?: number; // Optional: Initial retry delay in ms (default: 1000)
flushOnShutdown?: boolean; // Optional: Auto-flush on SIGTERM/SIGINT (default: true)
logger?: Logger; // Optional: Custom logger (default: console with [Klime] prefix)
onError?: (error, events) => void; // Optional: Callback for batch failures
onSuccess?: (response) => void; // Optional: Callback for successful sends
})Methods
track(event: string, properties?: object, options?: { userId?, groupId? })
Track an event. Events can be attributed in two ways:
- User events: Provide
userIdto track user activity (most common) - Group events: Provide
groupIdwithoutuserIdfor organization-level events
// User event (most common)
client.track(
"Button Clicked",
{
buttonName: "Sign up",
plan: "pro",
},
{ userId: "user_123" }
);
// Group event (for webhooks, cron jobs, system events)
client.track(
"Events Received",
{
count: 100,
source: "webhook",
},
{ groupId: "org_456" }
);Note: The
groupIdoption can also be combined withuserIdfor multi-tenant scenarios where you need to specify which organization context a user event occurred in.
identify(userId: string, traits?: object)
Identify a user with traits.
client.identify("user_123", {
email: "user@example.com",
name: "Stefan",
});group(groupId: string, traits?: object, options?: { userId? })
Associate a user with a group and/or set group traits.
// Associate user with a group and set group traits (most common)
client.group(
"org_456",
{ name: "Acme Inc", plan: "enterprise" },
{ userId: "user_123" }
);
// Just link a user to a group (traits already set or not needed)
client.group("org_456", null, { userId: "user_123" });
// Just update group traits (e.g., from a webhook or background job)
client.group("org_456", { plan: "enterprise", employeeCount: 50 });flush(): Promise<void>
Manually flush queued events immediately.
await client.flush();shutdown(): Promise<void>
Gracefully shutdown the client, flushing remaining events.
await client.shutdown();getQueueSize(): number
Return the number of events currently in the queue.
const pending = client.getQueueSize();
console.log(`${pending} events waiting to be sent`);Synchronous Methods
For cases where you need confirmation that events were sent (e.g., before process exit, in tests), use the synchronous variants:
trackSync(event, properties, options): Promise<BatchResponse>
Track an event synchronously. Returns BatchResponse or throws SendError.
const { SendError } = require("@klime/node");
try {
const response = await client.trackSync(
"Critical Action",
{ key: "value" },
{ userId: "user_123" }
);
console.log(`Sent! Accepted: ${response.accepted}`);
} catch (error) {
if (error instanceof SendError) {
console.error(`Failed to send: ${error.message}`);
}
}identifySync(userId, traits): Promise<BatchResponse>
Identify a user synchronously. Returns BatchResponse or throws SendError.
groupSync(groupId, traits, options): Promise<BatchResponse>
Associate a user with a group synchronously. Returns BatchResponse or throws SendError.
Features
- Automatic Batching: Events are automatically batched and sent every 2 seconds or when the batch size reaches 20 events
- Automatic Retries: Failed requests are automatically retried with exponential backoff
- Process Exit Handling: Automatically flushes events on SIGTERM/SIGINT
- Zero Dependencies: Uses only Node.js standard library (fetch for Node 18+, https/http for older versions)
- Universal Module Support: Works with ESM and CommonJS out of the box
Performance
When you call track(), identify(), or group(), the SDK:
- Adds the event to an in-memory queue (microseconds)
- Returns immediately without waiting for network I/O
Events are sent to Klime's servers asynchronously via Node.js's event loop. This means:
- No network blocking: HTTP requests happen asynchronously without blocking the event loop
- No latency impact: Tracking calls add < 1ms to your request handling time
- Automatic batching: Events are queued and sent in batches (default: every 2 seconds or 20 events)
// This returns immediately - no HTTP request is made here
client.track("Button Clicked", { button: "signup" }, { userId: "user_123" });
// Your code continues without waiting
res.json({ success: true });The only blocking operation is await flush(), which waits for all queued events to be sent. This is typically only called during graceful shutdown.
Configuration
Default Values
flushInterval: 2000msmaxBatchSize: 20 eventsmaxQueueSize: 1000 eventsretryMaxAttempts: 5 attemptsretryInitialDelay: 1000msflushOnShutdown: true
Logging
The SDK uses a console wrapper with [Klime] prefix by default. You can provide a custom logger:
const client = new KlimeClient({
writeKey: "your-write-key",
logger: {
debug: (msg, ...args) => myLogger.debug(msg, ...args),
info: (msg, ...args) => myLogger.info(msg, ...args),
warn: (msg, ...args) => myLogger.warn(msg, ...args),
error: (msg, ...args) => myLogger.error(msg, ...args),
},
});Callbacks
const client = new KlimeClient({
writeKey: "your-write-key",
onError: (error, events) => {
// Report to your error tracking service
Sentry.captureException(error);
console.error(`Failed to send ${events.length} events: ${error.message}`);
},
onSuccess: (response) => {
console.log(`Sent ${response.accepted} events`);
},
});Error Handling
The SDK automatically handles:
- Transient errors (429, 503, network failures): Retries with exponential backoff
- Permanent errors (400, 401): Logs error and drops event
- Rate limiting: Respects
Retry-Afterheader
For synchronous operations, use *Sync() methods which throw SendError on failure:
const { KlimeClient, SendError } = require("@klime/node");
try {
const response = await client.trackSync("Event", {}, { userId: "user_123" });
} catch (error) {
if (error instanceof SendError) {
console.error(`Failed: ${error.message}, events: ${error.events.length}`);
}
}