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 to enqueue before flushing a batch. 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 | Retry attempts for failed batches. Default: 3. | |
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": "6aeda8b77e0c0955",
"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({
"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": "3c075dc6680b8b38",
"trigger": "load",
"entity": "product",
"action": "view",
"timestamp": 1700000108,
"timing": 3.14,
"source": {
"count": 1,
"trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
"type": "collector",
"schema": "4"
}
}analytics.track({
"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": "1c7c68b0433d4036",
"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({
"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": "2994e48c5a766d14",
"trigger": "load",
"entity": "product",
"action": "view",
"timestamp": 1700000100,
"timing": 3.14,
"source": {
"count": 1,
"trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
"type": "collector",
"schema": "4"
}
}analytics.track({
"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": "cae580c795de91b5",
"trigger": "load",
"entity": "page",
"action": "view",
"timestamp": 1700000102,
"timing": 3.14,
"source": {
"count": 1,
"trace": "0a1b2c3d4e5f60718293a4b5c6d7e8f9",
"type": "collector",
"schema": "4"
}
}analytics.identify({
"userId": "us3r",
"anonymousId": "s3ss10n",
"traits": {
"email": "user@example.com"
},
"timestamp": {}
});
analytics.track({
"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,
"total": 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": "34d1e3c488919512",
"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({
"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": "bd470d5f0e133d51",
"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({
"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": "e10ba5488083cd5e",
"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({
"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": "95fbf22b325c4a01",
"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({
"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.
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.