npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@klime/node

v1.3.0

Published

Klime SDK for Node.js

Downloads

228

Readme

@klime/node

Klime SDK for Node.js.

Installation

npm install @klime/node

Quick 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: "[email protected]",
  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);
});

Good to know

  • No anonymous tracking. Every track() call needs a userId or groupId. 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 email and name traits in identify() and group(). 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 from identify() and track() are recorded, but customers won't appear until users are linked to groups.
  • Order doesn't matter. Events sent before identify() or group() are retroactively attributed once the relationships are established.

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: "[email protected]", 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

API 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 userId to track user activity (most common)
  • Group events: Provide groupId without userId for 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 groupId option can also be combined with userId for 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: "[email protected]",
  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

Module Compatibility

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.

Performance

When you call track(), identify(), or group(), the SDK:

  1. Adds the event to an in-memory queue (microseconds)
  2. 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: 2000ms
  • maxBatchSize: 20 events
  • maxQueueSize: 1000 events
  • retryMaxAttempts: 5 attempts
  • retryInitialDelay: 1000ms
  • flushOnShutdown: 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-After header

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}`);
  }
}

Size Limits

  • Maximum event size: 200KB
  • Maximum batch size: 10MB
  • Maximum events per batch: 100

Events exceeding these limits are rejected and logged.

Express.js Example

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);
});

Requirements

  • Node.js 14.0.0 or higher
  • For Node.js 18+, native fetch is used
  • For Node.js 14-17, https/http modules are used

License

MIT