Node.js SDK
Server-side tracking for Node.js. Events are queued in memory, batched, and delivered
asynchronously to /v1/batch with your write key — your request path never blocks on
analytics.
Installation
Section titled “Installation”npm install @origamy/node-sdkBasic usage
Section titled “Basic usage”import { New, Track } from "@origamy/node-sdk";
const client = New(process.env.ORIGAMY_WRITE_KEY);
client.enqueue(new Track({ userId: "user-123", event: "Signed Up",}));
// Flush any queued messages and close the client.await client.close();By default the client flushes every 5 seconds or 250 events, whichever comes first, and sends
to https://events.origamy.io.
Configuration
Section titled “Configuration”NewWithConfig validates the config and returns a Go-style [client, err] tuple:
import { NewWithConfig } from "@origamy/node-sdk";
const [client, err] = NewWithConfig(process.env.ORIGAMY_WRITE_KEY, { endpoint: "https://events.yourcompany.com", // your gateway on a self-hosted data plane verbose: process.env.ORIGAMY_VERBOSE === "true",});if (err) throw err;| Option | Default | Description |
|---|---|---|
endpoint |
https://events.origamy.io |
Ingestion endpoint. Point at your own gateway on a self-hosted data plane. |
intervalMs |
5000 |
Flush interval. |
batchSize |
250 |
Events per batch before an early flush. |
verbose |
false |
Log deliveries and errors. |
logger |
console | Custom logger implementation. |
dispatcher |
HTTP | Custom transport — see development mode below. |
queue / queueCapacity |
in-memory | Custom or bounded queue. |
defaultContext |
— | Context merged into every event. |
retryAfter |
exponential | (attempt) => ms backoff strategy. |
maxConcurrentRequests |
— | Cap on in-flight batch requests. |
Message types
Section titled “Message types”The message types mirror the event spec — construct one and enqueue it:
import { New, Track, Identify, Page, Group, Alias, NewProperties, NewTraits,} from "@origamy/node-sdk";
const client = New(process.env.ORIGAMY_WRITE_KEY);
// Track an actionclient.enqueue(new Track({ userId: "user-123", event: "Order Completed", properties: NewProperties() .set("orderId", "ORD-9999") .setRevenue(99.99) .setCurrency("USD"),}));
// Identify a user with traitsclient.enqueue(new Identify({ userId: "user-123", traits: NewTraits() .setName("Alice Smith") .set("plan", "pro"),}));
// Track a page viewclient.enqueue(new Page({ userId: "user-123", name: "Pricing", properties: { url: "https://example.com/pricing" },}));
// Associate a user with a group/companyclient.enqueue(new Group({ userId: "user-123", groupId: "company-acme", traits: NewTraits().setName("Acme Corp"),}));
// Alias an anonymous ID to an identified userclient.enqueue(new Alias({ userId: "user-123", previousId: "anon-session-abc",}));For anonymous tracking, pass anonymousId instead of (or alongside) userId.
Development mode
Section titled “Development mode”Use the NoopDispatcher to log events to the console instead of sending them:
import { NewWithConfig, NoopDispatcher, Track } from "@origamy/node-sdk";
const [client, err] = NewWithConfig("your-write-key", { dispatcher: new NoopDispatcher({ verbose: true }),});if (err) throw err;
client.enqueue(new Track({ userId: "user-123", event: "button_clicked" }));await client.close();Graceful shutdown
Section titled “Graceful shutdown”Always close the client before the process exits so queued events are flushed:
process.on("SIGTERM", async () => { await client.close(); process.exit(0);});Use with NestJS
Section titled “Use with NestJS”Wrap the client in an injectable service so tracking is one call anywhere in your app, and
flushing happens on shutdown via OnModuleDestroy:
import { Injectable, Logger, OnModuleDestroy } from "@nestjs/common";import { Client, NewWithConfig, Track, Identify } from "@origamy/node-sdk";
@Injectable()export class AnalyticsService implements OnModuleDestroy { private readonly logger = new Logger(AnalyticsService.name); private readonly client: Client | null;
constructor() { const [client, err] = NewWithConfig(process.env.ORIGAMY_WRITE_KEY, { endpoint: process.env.ORIGAMY_HOST || "https://events.origamy.io", verbose: process.env.ORIGAMY_VERBOSE === "true", }); if (err) { this.logger.error(`Failed to init Origamy client: ${err.message}`); this.client = null; } else { this.client = client; } }
identify(userId: string, traits?: Record<string, unknown>): void { if (!this.client || !userId) return; this.client.enqueue(new Identify({ userId, traits })); }
track(userId: string, event: string, properties?: Record<string, unknown>): void { if (!this.client || !userId) return; this.client.enqueue(new Track({ userId, event, properties })); }
async onModuleDestroy(): Promise<void> { await this.client?.close(); }}// anywhere in your appthis.analyticsService.identify(userId, { plan: "pro" });this.analyticsService.track(userId, "Order Completed", { amount: 99.99 });Wire format
Section titled “Wire format”Batches post to POST /v1/batch using the same payload as every Origamy SDK, authenticated
with HTTP Basic auth (write key as username, empty password). See the
Ingestion API for endpoints, limits, and response codes.
