RudderStack
Server-side event delivery to RudderStack via the official @rudderstack/rudder-sdk-node SDK. Implements the full Segment Spec surface (track, identify, group, page, screen, and alias) with automatic identity deduplication and graceful shutdown via flush().
RudderStack is a server destination in the walkerOS flow:
Receives events server-side from the collector, resolves identity, and forwards them to RudderStack's data plane for routing to downstream destinations.
Installation
npm install @walkeros/server-destination-rudderstack- Integrated
- Bundled
import { startFlow } from '@walkeros/collector';
import { destinationRudderStack } from '@walkeros/server-destination-rudderstack';
await startFlow({
destinations: {
rudderstack: {
code: destinationRudderStack,
config: {
settings: {
writeKey: 'YOUR_RUDDERSTACK_WRITE_KEY',
dataPlaneUrl: 'https://your-data-plane.rudderstack.com',
},
},
},
},
});Add to your flow.json destinations:
"destinations": {
"rudderstack": {
"package": "@walkeros/server-destination-rudderstack",
"config": {
"settings": {
"writeKey": "YOUR_RUDDERSTACK_WRITE_KEY",
"dataPlaneUrl": "https://your-data-plane.rudderstack.com"
}
}
}
}Configuration
This destination uses the standard destination config wrapper (consent, data, env, id, ...). For the shared fields see destination configuration. Package-specific fields live under config.settings and are listed below.
Settings
| Property | Type | Description | More |
|---|---|---|---|
writeKey | string | RudderStack source write key. Find it in the RudderStack dashboard under Sources > Setup. | |
dataPlaneUrl | string | RudderStack data plane URL. Required. Example: https://your-data-plane.rudderstack.com | |
userId | string | walkerOS mapping value path to resolve userId from each event (like user.id). | |
anonymousId | string | walkerOS mapping value path to resolve anonymousId from each event (like user.session). | |
path | string | API path route (like /v1/batch). | |
flushAt | integer | Events the SDK sends in one request. Each push and each config.batch batch flushes the SDK and waits until its calls are sent. Default: 20. | |
flushInterval | integer | Max milliseconds before auto-flush. Default: 10000. | |
maxQueueSize | integer | Maximum batch payload size in bytes. Default: 460800 (~500KB). | |
maxInternalQueueSize | integer | Maximum in-memory queue length. Default: 20000. | |
logLevel | string | SDK log level: 'info', 'debug', 'error', or 'silly'. Default: 'info'. | |
retryCount | integer | Retries the SDK makes for a failed request, a tenth of config.timeout apart. Default: 2, which ends within config.timeout. A value set here wins. | |
timeout | integer | Time one request may take, in milliseconds. Default: a quarter of config.timeout (2500 at the 10 s default). A value set here wins. | |
enable | boolean | Set to false to no-op all SDK calls. Default: true. | |
gzip | boolean | Enable gzip compression for requests. Default: true. | |
identify | any | Destination-level identity mapping. Resolves to { traits } object. Fires identify() on the first push and re-fires when values change. | |
group | any | Destination-level group mapping. Resolves to { groupId, traits }. Fires group() on the first push and re-fires on change. | |
integrations | Record<string, any> | Enable/disable downstream RudderStack destinations. Example: { "All": true, "Mixpanel": false }. |
Mapping
Per-event rules under config.mapping. For the standard rule fields (consent, condition, data, batch, name, policy) see mapping.
| Property | Type | Description | More |
|---|---|---|---|
identify | any | Per-event identity mapping. Resolves to { userId?, traits? }. Use with rule-level silent: true on login/identify events. | |
group | any | Per-event group assignment. Resolves to { groupId, traits? }. Use with rule-level silent: true on company/team events. | |
page | any | Per-event page call. Resolves to { name, properties? } or true for minimal page(). RudderStack requires name (falls back to empty string). Use with silent: true. | |
screen | any | Per-event screen call (mobile backends). Resolves to { name, properties? } or true for minimal screen(). RudderStack requires name (falls back to empty string). Use with silent: true. | |
alias | any | Per-event alias call. Resolves to { previousId }. Links anonymous user to identified user. Use with rule-level silent: true. |
Examples
Alias user
An identity merge fires analytics.alias to link a previous anonymous id to the newly registered user id.
{
"name": "identity merge",
"data": {
"anonymous_id": "anonymous-123"
},
"context": {
"dev": [
"test",
1
]
},
"globals": {
"lang": "elb"
},
"custom": {
"completely": "random"
},
"user": {
"id": "registered-456",
"session": "s3ss10n"
},
"nested": [
{
"entity": "child",
"data": {
"is": "subordinated"
}
}
],
"consent": {
"functional": true
},
"id": "ev1700000107",
"trigger": "test",
"entity": "identity",
"action": "merge",
"timestamp": 1700000107,
"timing": 3.14,
"source": {
"count": 1,
"trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
"type": "collector",
"schema": "4"
}
}{
"silent": true,
"settings": {
"alias": {
"map": {
"previousId": "data.anonymous_id"
}
}
}
}analytics.alias({
"messageId": "ev1700000107-alias",
"userId": "registered-456",
"previousId": "anonymous-123",
"timestamp": {}
})Anonymous only
When no userId is resolved RudderStack accepts the track call keyed solely by anonymousId.
{
"name": "product view",
"data": {
"id": "ers",
"name": "Everyday Ruck Snack",
"color": "black",
"size": "l",
"price": 420
},
"context": {
"shopping": [
"detail",
0
]
},
"globals": {
"pagegroup": "shop"
},
"custom": {
"completely": "random"
},
"user": {
"session": "s3ss10n"
},
"nested": [],
"consent": {
"functional": true
},
"id": "ev1700000108",
"trigger": "load",
"entity": "product",
"action": "view",
"timestamp": 1700000108,
"timing": 3.14,
"source": {
"count": 1,
"trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
"type": "collector",
"schema": "4"
}
}analytics.track({
"messageId": "ev1700000108",
"anonymousId": "s3ss10n",
"event": "product view",
"properties": {},
"timestamp": {}
})Group company
A company update fires analytics.group with the groupId and group traits for B2B account tracking.
{
"name": "company update",
"data": {
"company_id": "comp-456",
"company_name": "Acme",
"industry": "tech",
"employees": 50
},
"context": {
"dev": [
"test",
1
]
},
"globals": {
"lang": "elb"
},
"custom": {
"completely": "random"
},
"user": {
"id": "us3r",
"session": "s3ss10n"
},
"nested": [
{
"entity": "child",
"data": {
"is": "subordinated"
}
}
],
"consent": {
"functional": true
},
"id": "ev1700000104",
"trigger": "test",
"entity": "company",
"action": "update",
"timestamp": 1700000104,
"timing": 3.14,
"source": {
"count": 1,
"trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
"type": "collector",
"schema": "4"
}
}{
"silent": true,
"settings": {
"group": {
"map": {
"groupId": "data.company_id",
"traits": {
"map": {
"name": "data.company_name",
"industry": "data.industry",
"employees": "data.employees"
}
}
}
}
}
}analytics.group({
"messageId": "ev1700000104-group",
"userId": "us3r",
"anonymousId": "s3ss10n",
"groupId": "comp-456",
"traits": {
"name": "Acme",
"industry": "tech",
"employees": 50
},
"timestamp": {}
})Default track
A walker event becomes a RudderStack analytics.track call with userId and anonymousId resolved from the event.
{
"name": "product view",
"data": {
"id": "ers",
"name": "Everyday Ruck Snack",
"color": "black",
"size": "l",
"price": 420
},
"context": {
"shopping": [
"detail",
0
]
},
"globals": {
"pagegroup": "shop"
},
"custom": {
"completely": "random"
},
"user": {
"id": "us3r",
"session": "s3ss10n"
},
"nested": [],
"consent": {
"functional": true
},
"id": "ev1700000100",
"trigger": "load",
"entity": "product",
"action": "view",
"timestamp": 1700000100,
"timing": 3.14,
"source": {
"count": 1,
"trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
"type": "collector",
"schema": "4"
}
}analytics.track({
"messageId": "ev1700000100",
"userId": "us3r",
"anonymousId": "s3ss10n",
"event": "product view",
"properties": {},
"timestamp": {}
})Destination identify
Destination-level identify fires analytics.identify with traits on the first push only, then the track call follows.
{
"name": "page view",
"data": {
"domain": "www.example.com",
"title": "walkerOS documentation",
"referrer": "https://www.walkeros.io/",
"search": "?foo=bar",
"hash": "#hash",
"id": "/docs/"
},
"context": {
"dev": [
"test",
1
]
},
"globals": {
"pagegroup": "docs"
},
"custom": {
"completely": "random"
},
"user": {
"id": "us3r",
"session": "s3ss10n",
"email": "user@example.com"
},
"nested": [
{
"entity": "child",
"data": {
"is": "subordinated"
}
}
],
"consent": {
"functional": true
},
"id": "ev1700000102",
"trigger": "load",
"entity": "page",
"action": "view",
"timestamp": 1700000102,
"timing": 3.14,
"source": {
"count": 1,
"trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
"type": "collector",
"schema": "4"
}
}analytics.identify({
"messageId": "ev1700000102-identify",
"userId": "us3r",
"anonymousId": "s3ss10n",
"traits": {
"email": "user@example.com"
},
"timestamp": {}
});
analytics.track({
"messageId": "ev1700000102",
"userId": "us3r",
"anonymousId": "s3ss10n",
"event": "page view",
"properties": {},
"timestamp": {}
})Renamed event
A mapping renames the event so the RudderStack track call uses a canonical name such as Order Completed.
{
"name": "order complete",
"data": {
"id": "0rd3r1d",
"currency": "EUR",
"shipping": 5.22,
"taxes": 73.76,
"value": 555
},
"context": {
"shopping": [
"complete",
0
]
},
"globals": {
"pagegroup": "shop"
},
"custom": {
"completely": "random"
},
"user": {
"id": "us3r",
"session": "s3ss10n"
},
"nested": [
{
"entity": "product",
"data": {
"id": "ers",
"name": "Everyday Ruck Snack",
"color": "black",
"size": "l",
"price": 420
},
"context": {
"shopping": [
"complete",
0
]
},
"nested": []
},
{
"entity": "product",
"data": {
"id": "cc",
"name": "Cool Cap",
"size": "one size",
"price": 42
},
"context": {
"shopping": [
"complete",
0
]
},
"nested": []
},
{
"entity": "gift",
"data": {
"name": "Surprise"
},
"context": {
"shopping": [
"complete",
0
]
},
"nested": []
}
],
"consent": {
"functional": true
},
"id": "ev1700000101",
"trigger": "load",
"entity": "order",
"action": "complete",
"timestamp": 1700000101,
"timing": 3.14,
"source": {
"count": 1,
"trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
"type": "collector",
"schema": "4"
}
}{
"name": "Order Completed"
}analytics.track({
"messageId": "ev1700000101",
"userId": "us3r",
"anonymousId": "s3ss10n",
"event": "Order Completed",
"properties": {},
"timestamp": {}
})Page view
A page view fires analytics.page with a resolved page name and properties instead of a generic track.
{
"name": "page view",
"data": {
"title": "Getting Started",
"section": "tutorials"
},
"context": {
"dev": [
"test",
1
]
},
"globals": {
"pagegroup": "docs"
},
"custom": {
"completely": "random"
},
"user": {
"id": "us3r",
"session": "s3ss10n"
},
"nested": [
{
"entity": "child",
"data": {
"is": "subordinated"
}
}
],
"consent": {
"functional": true
},
"id": "ev1700000105",
"trigger": "load",
"entity": "page",
"action": "view",
"timestamp": 1700000105,
"timing": 3.14,
"source": {
"count": 1,
"trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
"type": "collector",
"schema": "4"
}
}{
"silent": true,
"settings": {
"page": {
"map": {
"name": "data.title",
"properties": {
"map": {
"section": "data.section"
}
}
}
}
}
}analytics.page({
"messageId": "ev1700000105-page",
"userId": "us3r",
"anonymousId": "s3ss10n",
"name": "Getting Started",
"properties": {
"section": "tutorials"
},
"timestamp": {}
})Screen view
A screen view from a mobile app backend fires analytics.screen with a screen name and properties.
{
"name": "screen view",
"data": {
"screen_name": "Welcome",
"section": "onboarding",
"build": "1.2.3"
},
"context": {
"dev": [
"test",
1
]
},
"globals": {
"lang": "elb"
},
"custom": {
"completely": "random"
},
"user": {
"id": "us3r",
"session": "s3ss10n"
},
"nested": [
{
"entity": "child",
"data": {
"is": "subordinated"
}
}
],
"consent": {
"functional": true
},
"id": "ev1700000106",
"trigger": "test",
"entity": "screen",
"action": "view",
"timestamp": 1700000106,
"timing": 3.14,
"source": {
"count": 1,
"trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
"type": "collector",
"schema": "4"
}
}{
"silent": true,
"settings": {
"screen": {
"map": {
"name": "data.screen_name",
"properties": {
"map": {
"build": "data.build"
}
}
}
}
}
}analytics.screen({
"messageId": "ev1700000106-screen",
"userId": "us3r",
"anonymousId": "s3ss10n",
"name": "Welcome",
"properties": {
"build": "1.2.3"
},
"timestamp": {}
})User login identify
A user login fires only an analytics.identify call with the resolved userId and traits, skipping the track.
{
"name": "user login",
"data": {
"user_id": "new-user-123",
"email": "user@acme.com",
"name": "Jane Doe",
"plan": "premium"
},
"context": {
"dev": [
"test",
1
]
},
"globals": {
"lang": "elb"
},
"custom": {
"completely": "random"
},
"user": {
"id": "us3r",
"session": "s3ss10n"
},
"nested": [
{
"entity": "child",
"data": {
"is": "subordinated"
}
}
],
"consent": {
"functional": true
},
"id": "ev1700000103",
"trigger": "test",
"entity": "user",
"action": "login",
"timestamp": 1700000103,
"timing": 3.14,
"source": {
"count": 1,
"trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
"type": "collector",
"schema": "4"
}
}{
"silent": true,
"settings": {
"identify": {
"map": {
"userId": "data.user_id",
"traits": {
"map": {
"email": "data.email",
"name": "data.name",
"plan": "data.plan"
}
}
}
}
}
}analytics.identify({
"messageId": "ev1700000103-identify",
"userId": "new-user-123",
"anonymousId": "s3ss10n",
"traits": {
"email": "user@acme.com",
"name": "Jane Doe",
"plan": "premium"
},
"timestamp": {}
})Identity is resolved automatically from each event: userId defaults to user.id and anonymousId defaults to user.session. At least one must be present for a call to be sent. Override these paths in settings to match your event structure.
Batching and delivery
This destination batches by default: config.batch sends after 20 events or 10 seconds, and each event keeps its own outcome. Set config.batch: { size: 1 } to send each event at once. How batching, retries, deduplication and acknowledgement work for the server SDK destinations is described in Server SDK destinations.
An event without userId or anonymousId fails with missing-input.
Alias support
Unlike Segment, RudderStack also supports per-event alias() calls, useful for linking an anonymous identity to an identified user after signup or login. Configure via mapping.settings.alias with a previousId field.