Node.js SDK
The Dreamdata Node.js SDK (@segment/analytics-node) provides a robust way to
track events from your server-side applications. It handles batching, retries,
and graceful shutdown automatically.
Runtime Support
The SDK is compatible with:
- Node.js >= 18
- AWS Lambda
- Cloudflare Workers
- Vercel Edge Functions
- Web Workers (experimental)
Prerequisites
Before you begin, ensure you have:
- A Dreamdata account
- Your Dreamdata Source API Key (found in
Data Platform > Sources > Server Side Analytics APIs)
Installation
Install the SDK using your preferred package manager:
# npm
npm install @segment/analytics-node
# yarn
yarn add @segment/analytics-node
# pnpm
pnpm install @segment/analytics-node
Quick Start
import { Analytics } from "@segment/analytics-node";
// Initialize the Analytics client
const analytics = new Analytics({
writeKey: "<YOUR_DREAMDATA_API_KEY>",
});
// Track an event
analytics.track({
userId: "user-123",
event: "Order Completed",
properties: {
orderId: "order-456",
revenue: 99.99,
},
});
// Identify a user
analytics.identify({
userId: "user-123",
traits: {
email: "user@example.com",
name: "Jane Doe",
plan: "enterprise",
},
});
Using CommonJS
const { Analytics } = require("@segment/analytics-node");
const analytics = new Analytics({
writeKey: "<YOUR_DREAMDATA_API_KEY>",
});
API Methods
identify()
Associates a user with their traits and records when they are first recognized.
analytics.identify({
userId: "user-123",
traits: {
email: "user@example.com",
name: "Jane Doe",
company: "Acme Inc",
plan: "enterprise",
},
});
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
userId | string | Yes* | Unique identifier for the user |
anonymousId | string | Yes* | Anonymous identifier (if userId not available) |
traits | object | No | User attributes (email, name, company, etc.) |
context | object | No | Extra context (ip, userAgent, campaign, etc.) |
timestamp | ISO Date | No | When the event occurred (defaults to now) |
integrations | object | No | Control which integrations receive this event |
*At least one of userId or anonymousId is required.
track()
Records actions your users perform.
analytics.track({
userId: "user-123",
event: "Article Read",
properties: {
title: "Getting Started with B2B Analytics",
category: "Documentation",
readTime: 245,
},
});
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
userId | string | Yes* | Unique identifier for the user |
anonymousId | string | Yes* | Anonymous identifier |
event | string | Yes | Name of the action being tracked |
properties | object | No | Properties describing the event |
context | object | No | Extra context |
timestamp | ISO Date | No | When the event occurred |
integrations | object | No | Control integrations |
page()
Records page views on your website.
analytics.page({
userId: "user-123",
category: "Docs",
name: "API Reference",
properties: {
url: "https://example.com/docs/api",
referrer: "https://google.com",
},
});
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
userId | string | Yes* | Unique identifier for the user |
anonymousId | string | Yes* | Anonymous identifier |
category | string | No | Category of the page |
name | string | No | Name of the page |
properties | object | No | Page properties (url, referrer, title, etc.) |
context | object | No | Extra context |
timestamp | ISO Date | No | When the page was viewed |
group()
Associates a user with a company or organization.
analytics.group({
userId: "user-123",
groupId: "company-456",
traits: {
name: "Acme Inc",
industry: "Technology",
employees: 150,
plan: "enterprise",
},
});
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
userId | string | Yes* | Unique identifier for the user |
anonymousId | string | Yes* | Anonymous identifier |
groupId | string | Yes | Unique identifier for the group/company |
traits | object | No | Group attributes |
context | object | No | Extra context |
timestamp | ISO Date | No | When the association occurred |
alias()
Merges two user identities, connecting a known user to an anonymous one.
analytics.alias({
userId: "user-123",
previousId: "anon-xyz-789",
});
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
userId | string | Yes | The new user ID |
previousId | string | Yes | The previous ID (userId or anonymousId) |
context | object | No | Extra context |
timestamp | ISO Date | No | When the alias occurred |
screen()
Records screen views in mobile or desktop applications.
analytics.screen({
userId: "user-123",
category: "Settings",
name: "Profile",
properties: {
variation: "dark-mode",
},
});
Configuration
The Analytics constructor accepts a settings object with the following
options:
const analytics = new Analytics({
writeKey: "<YOUR_API_KEY>",
host: "https://cdn.dreamdata.cloud/api",
path: "/v1/batch",
flushAt: 15,
flushInterval: 10000,
maxRetries: 3,
httpRequestTimeout: 10000,
disable: false,
});
| Option | Type | Default | Description |
|---|---|---|---|
writeKey | string | Required | Your Dreamdata API key |
host | string | https://cdn.dreamdata.cloud/api | API endpoint |
path | string | /v1/batch | API path |
flushAt | number | 15 | Number of events to queue before sending (alias: maxEventsInBatch) |
flushInterval | number | 10000 | Milliseconds to wait before flushing (10 seconds) |
maxRetries | number | 3 | Number of retry attempts for failed requests |
httpRequestTimeout | number | 10000 | Request timeout in milliseconds |
disable | boolean | false | Disable all tracking (useful for development) |
httpClient | object | Built-in fetch client | Custom HTTP client implementation |
oauthSettings | object | undefined | OAuth 2 configuration (see advanced section) |
Error Handling
The SDK emits events that you can listen to for debugging and error handling:
const analytics = new Analytics({
writeKey: "<YOUR_API_KEY>",
});
// Handle errors
analytics.on("error", (err) => {
console.error("Analytics error:", err);
});
// Debug HTTP requests
analytics.on("http_request", (request) => {
console.log("Request:", request.url, request.method);
});
// Detect events sent after close
analytics.on("call_after_close", (event) => {
console.warn("Event sent after analytics was closed:", event);
});
Flushing and Graceful Shutdown
flush()
Manually flush all queued events without closing the client:
await analytics.flush();
closeAndFlush()
Flush all events and close the client. Call this before your application exits:
// Graceful shutdown
process.on("SIGTERM", async () => {
await analytics.closeAndFlush();
process.exit(0);
});
Both methods accept an optional timeout parameter:
// Wait up to 5 seconds for flush to complete
await analytics.closeAndFlush({ timeout: 5000 });
Serverless Environments
Serverless environments require special handling because the runtime may
terminate before events are sent. The key is to await the callback or use
flushAt: 1 to send events immediately.
AWS Lambda
import { Analytics } from "@segment/analytics-node";
// Create a new instance per request to avoid state issues
const createAnalytics = () =>
new Analytics({
writeKey: "<YOUR_API_KEY>",
flushAt: 1, // Send immediately
}).on("error", console.error);
export const handler = async (event) => {
const analytics = createAnalytics();
// Await the callback to ensure the event is sent
await new Promise((resolve) =>
analytics.track(
{
userId: event.userId,
event: "Lambda Invoked",
properties: { functionName: "my-function" },
},
resolve,
),
);
return {
statusCode: 200,
body: JSON.stringify({ success: true }),
};
};
Vercel Edge Functions
import { Analytics } from "@segment/analytics-node";
import { NextRequest, NextResponse } from "next/server";
const analytics = new Analytics({
writeKey: "<YOUR_API_KEY>",
flushAt: 1,
}).on("error", console.error);
export const config = {
runtime: "edge",
};
export default async (req: NextRequest) => {
await new Promise((resolve) =>
analytics.track(
{
userId: "user-123",
event: "Edge Function Called",
},
resolve,
),
);
return NextResponse.json({ success: true });
};
Cloudflare Workers
import { Analytics } from "@segment/analytics-node";
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const analytics = new Analytics({
writeKey: env.DREAMDATA_API_KEY,
flushAt: 1,
}).on("error", console.error);
await new Promise((resolve) =>
analytics.track(
{
userId: "user-123",
event: "Worker Invoked",
},
resolve,
),
);
return new Response(JSON.stringify({ success: true }), {
headers: { "Content-Type": "application/json" },
});
},
};
OAuth 2 Authentication
For enhanced security between your server and Dreamdata's Tracking API, you can enable OAuth 2 authentication. This uses a signed client assertion JWT for non-interactive server environments.
Setup
- Enable OAuth 2 in your Dreamdata workspace
- Upload your public key to the Dreamdata dashboard
- Keep your private key secure on your server
import { Analytics, OAuthSettings } from "@segment/analytics-node";
import { readFileSync } from "fs";
const privateKey = readFileSync("private.pem", "utf8");
const oauthSettings: OAuthSettings = {
clientId: "<CLIENT_ID_FROM_DASHBOARD>",
clientKey: privateKey,
keyId: "<PUBLIC_KEY_ID_FROM_DASHBOARD>",
};
const analytics = new Analytics({
writeKey: "<YOUR_API_KEY>",
oauthSettings,
});
analytics.on("error", (err) => {
console.error("OAuth or tracking error:", err);
});
analytics.track({
userId: "user-123",
event: "Secure Event",
});
OAuth Settings
| Option | Type | Required | Description |
|---|---|---|---|
clientId | string | Yes | OAuth App ID from Dreamdata dashboard |
clientKey | string | Yes | Your private key (PEM format) |
keyId | string | Yes | Public key ID from Dreamdata dashboard |
authServer | string | No | Authorization server URL |
scope | string | No | Permission scope (default: tracking_api:write) |
maxRetries | number | No | Token request retry attempts |
httpClient | object | No | Custom HTTP client for auth requests |
Generating Identifiers
For purely server-side integrations, you need to generate and manage user
identifiers yourself. The SDK requires either userId or anonymousId for
every event.
Using userId
Use userId when you have an authenticated user. This should be a stable
identifier from your system (database ID, auth provider ID, etc.):
analytics.track({
userId: user.id, // From your auth system
event: "Feature Used",
properties: { feature: "export" },
});
Generating anonymousId
For anonymous visitors, generate a UUID and persist it across requests:
import { randomUUID } from "crypto";
// Generate a new anonymous ID
const anonymousId = randomUUID(); // e.g., "550e8400-e29b-41d4-a716-446655440000"
Maintaining Identity Across Requests
Store the anonymousId in a session or cookie to maintain consistency:
import express from "express";
import session from "express-session";
import { randomUUID } from "crypto";
import { Analytics } from "@segment/analytics-node";
const app = express();
const analytics = new Analytics({ writeKey: "<YOUR_API_KEY>" });
app.use(
session({
secret: "your-secret",
resave: false,
saveUninitialized: true,
}),
);
// Middleware to ensure anonymousId exists
app.use((req, res, next) => {
if (!req.session.anonymousId) {
req.session.anonymousId = randomUUID();
}
next();
});
app.get("/products/:id", (req, res) => {
analytics.track({
anonymousId: req.session.anonymousId,
event: "Product Viewed",
properties: { productId: req.params.id },
});
res.json({ product: req.params.id });
});
Linking Anonymous to Known Users
When a user logs in, call alias to connect their anonymous activity to their
known identity:
app.post("/login", async (req, res) => {
const user = await authenticateUser(req.body);
// Link anonymous ID to the authenticated user
analytics.alias({
userId: user.id,
previousId: req.session.anonymousId,
});
// Future events use userId
analytics.identify({
userId: user.id,
traits: { email: user.email, name: user.name },
});
res.json({ success: true });
});
Cookieless Tracking (Company-Level Only)
For privacy-first tracking without individual identification, use a static placeholder and rely on IP-to-company lookup:
analytics.track({
anonymousId: "cookieless", // Static placeholder
event: "Page Viewed",
context: {
ip: clientIp, // Required for company attribution
},
});
See Server-Side Cookieless Tracking for complete implementation details.
Cross-Domain Tracking
When your application spans multiple domains or subdomains, you need to ensure
the anonymousId persists across them to maintain a unified user journey.
Cross-Subdomain Tracking
To track users across subdomains (e.g., www.example.com → app.example.com),
set cookies at the top-level domain so they're shared:
import express from "express";
import cookieParser from "cookie-parser";
import { randomUUID } from "crypto";
const app = express();
app.use(cookieParser());
// Middleware for cross-subdomain anonymous ID
app.use((req, res, next) => {
const cookieName = "dd_anonymous_id";
if (!req.cookies[cookieName]) {
const anonId = randomUUID();
res.cookie(cookieName, anonId, {
domain: ".example.com", // Leading dot includes all subdomains
path: "/",
maxAge: 365 * 24 * 60 * 60 * 1000, // 1 year
sameSite: "lax",
secure: true, // Requires HTTPS
httpOnly: true,
});
req.anonymousId = anonId;
} else {
req.anonymousId = req.cookies[cookieName];
}
next();
});
Key cookie options:
| Option | Value | Purpose |
|---|---|---|
domain | .example.com | Leading dot shares cookie across subdomains |
path | / | Available on all paths |
sameSite | lax | Prevents CSRF while allowing navigation |
secure | true | Only sent over HTTPS |
httpOnly | true | Not accessible via JavaScript (more secure) |
Cross-TLD Tracking
Tracking across different top-level domains (e.g., example.com → example.io)
requires explicit coordination since cookies cannot be shared across TLDs.
Option 1: URL Parameter
Pass the anonymousId via URL when linking between domains:
// On source domain (example.com)
app.get("/go-to-partner", (req, res) => {
const targetUrl = new URL("https://example.io/landing");
targetUrl.searchParams.set("dd_anon_id", req.anonymousId);
res.redirect(targetUrl.toString());
});
// On target domain (example.io)
app.use((req, res, next) => {
const urlAnonId = req.query.dd_anon_id as string;
const cookieAnonId = req.cookies.dd_anonymous_id;
if (urlAnonId && !cookieAnonId) {
// Use the ID from the source domain
res.cookie("dd_anonymous_id", urlAnonId, {
domain: ".example.io",
path: "/",
maxAge: 365 * 24 * 60 * 60 * 1000,
sameSite: "lax",
secure: true,
});
req.anonymousId = urlAnonId;
} else {
req.anonymousId = cookieAnonId || randomUUID();
}
next();
});
Option 2: Shared Backend Lookup
If both domains share a backend or database, store the mapping:
// When user authenticates or performs a linking action
async function linkDomains(userId: string, anonymousId: string) {
await db.userIdentities.upsert({
where: { visitorSessionId: anonymousId },
create: {
visitorSessionId: anonymousId,
linkedAnonymousIds: [anonymousId],
},
update: { linkedAnonymousIds: { push: anonymousId } },
});
}
Cross-TLD tracking via URL parameters may expose the anonymous ID in browser history, logs, and referrer headers. Consider:
- Using short-lived tokens instead of raw IDs
- Implementing server-side redirects to strip parameters
- Ensuring compliance with privacy regulations
Best Practices
- Always handle errors - Attach an error listener to catch and log issues
- Use graceful shutdown - Call
closeAndFlush()before your application exits - Set appropriate flush settings - Balance between real-time data and efficiency
- Include context - Add IP addresses and user agents for better attribution
- Use consistent user IDs - Maintain the same
userIdacross your backend services
Example: Express.js Integration
import express from "express";
import { Analytics } from "@segment/analytics-node";
const app = express();
const analytics = new Analytics({
writeKey: "<YOUR_API_KEY>",
}).on("error", console.error);
app.use(express.json());
// Middleware to extract tracking context
app.use((req, res, next) => {
req.trackingContext = {
ip: req.ip,
userAgent: req.get("User-Agent"),
};
next();
});
app.post("/api/signup", (req, res) => {
const { email, name } = req.body;
const userId = generateUserId();
analytics.identify({
userId,
traits: { email, name },
context: req.trackingContext,
});
analytics.track({
userId,
event: "User Signed Up",
properties: { method: "email" },
context: req.trackingContext,
});
res.json({ userId });
});
// Graceful shutdown
process.on("SIGTERM", async () => {
await analytics.closeAndFlush();
process.exit(0);
});
app.listen(3000);