Klime SDK for Node.js.
npm install @klime/node// 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);
});- No anonymous tracking. Every
track()call needs auserIdorgroupId. Wait until the user is identified before sending events. - Name your events in past tense with an Object + Action pattern:
"Report Generated","User Invited","Export Completed". - Always set
emailandnametraits inidentify()andgroup(). These are used for display and search in the dashboard. - Traits can be strings, numbers, booleans, or ISO 8601 date strings. Use camelCase for trait keys.
- In Group mode, your dashboard stays empty until you call
group(). Events fromidentify()andtrack()are recorded, but customers won't appear until users are linked to groups. - Order doesn't matter. Events sent before
identify()orgroup()are retroactively attributed once the relationships are established.
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 questions
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
})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 a user with traits.
client.identify("user_123", {
email: "user@example.com",
name: "Stefan",
});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 });Manually flush queued events immediately.
await client.flush();Gracefully shutdown the client, flushing remaining events.
await client.shutdown();Return the number of events currently in the queue.
const pending = client.getQueueSize();
console.log(`${pending} events waiting to be sent`);For cases where you need confirmation that events were sent (e.g., before process exit, in tests), use the synchronous variants:
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}`);
}
}Identify a user synchronously. Returns BatchResponse or throws SendError.
Associate a user with a group synchronously. Returns BatchResponse or throws SendError.
- 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
This package ships dual ESM/CommonJS builds:
// ESM (recommended for modern projects)
import { KlimeClient } from "@klime/node";
// CommonJS
const { KlimeClient } = require("@klime/node");Works with all major Node.js frameworks including Express, Fastify, Hono, Next.js, and NestJS.
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.
flushInterval: 2000msmaxBatchSize: 20 eventsmaxQueueSize: 1000 eventsretryMaxAttempts: 5 attemptsretryInitialDelay: 1000msflushOnShutdown: true
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),
},
});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`);
},
});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}`);
}
}- Maximum event size: 200KB
- Maximum batch size: 10MB
- Maximum events per batch: 100
Events exceeding these limits are rejected and logged.
const express = require("express");
const { KlimeClient } = require("@klime/node");
const app = express();
const client = new KlimeClient({
writeKey: process.env.KLIME_WRITE_KEY,
});
app.post("/api/button-clicked", (req, res) => {
client.track(
"Button Clicked",
{
buttonName: req.body.buttonName,
},
{
userId: req.user.id,
}
);
res.json({ success: true });
});
// Graceful shutdown
process.on("SIGTERM", async () => {
await client.shutdown();
process.exit(0);
});- Node.js 14.0.0 or higher
- For Node.js 18+, native
fetchis used - For Node.js 14-17,
https/httpmodules are used
MIT