Skip to main content

RudderStack

Server Source code Package Beta

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().

Where this fits

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

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

PropertyTypeDescriptionMore
writeKey*stringRudderStack source write key. Find it in the RudderStack dashboard under Sources > Setup.
dataPlaneUrl*stringRudderStack data plane URL. Required. Example: https://your-data-plane.rudderstack.com
userIdstringwalkerOS mapping value path to resolve userId from each event (like user.id).
anonymousIdstringwalkerOS mapping value path to resolve anonymousId from each event (like user.session).
pathstringAPI path route (like /v1/batch).
flushAtintegerEvents to enqueue before flushing a batch. Default: 20.
flushIntervalintegerMax milliseconds before auto-flush. Default: 10000.
maxQueueSizeintegerMaximum batch payload size in bytes. Default: 460800 (~500KB).
maxInternalQueueSizeintegerMaximum in-memory queue length. Default: 20000.
logLevelstringSDK log level: 'info', 'debug', 'error', or 'silly'. Default: 'info'.
retryCountintegerRetry attempts for failed batches. Default: 3.
enablebooleanSet to false to no-op all SDK calls. Default: true.
gzipbooleanEnable gzip compression for requests. Default: true.
identifyanyDestination-level identity mapping. Resolves to { traits } object. Fires identify() on the first push and re-fires when values change.
groupanyDestination-level group mapping. Resolves to { groupId, traits }. Fires group() on the first push and re-fires on change.
integrationsRecord<string, any>Enable/disable downstream RudderStack destinations. Example: { "All": true, "Mixpanel": false }.
* Required fields

Mapping

Per-event rules under config.mapping. For the standard rule fields (consent, condition, data, batch, name, policy) see mapping.

PropertyTypeDescriptionMore
identifyanyPer-event identity mapping. Resolves to { userId?, traits? }. Use with rule-level silent: true on login/identify events.
groupanyPer-event group assignment. Resolves to { groupId, traits? }. Use with rule-level silent: true on company/team events.
pageanyPer-event page call. Resolves to { name, properties? } or true for minimal page(). RudderStack requires name (falls back to empty string). Use with silent: true.
screenanyPer-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.
aliasanyPer-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.

Event
{
  "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"
  }
}
Mapping
{
  "silent": true,
  "settings": {
    "alias": {
      "map": {
        "previousId": "data.anonymous_id"
      }
    }
  }
}
Out
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.

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": {
    "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"
  }
}
Out
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.

Event
{
  "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"
  }
}
Mapping
{
  "silent": true,
  "settings": {
    "group": {
      "map": {
        "groupId": "data.company_id",
        "traits": {
          "map": {
            "name": "data.company_name",
            "industry": "data.industry",
            "employees": "data.employees"
          }
        }
      }
    }
  }
}
Out
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.

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"
  }
}
Out
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.

Event
{
  "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"
  }
}
Out
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.

Event
{
  "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"
  }
}
Mapping
{
  "name": "Order Completed"
}
Out
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.

Event
{
  "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"
  }
}
Mapping
{
  "silent": true,
  "settings": {
    "page": {
      "map": {
        "name": "data.title",
        "properties": {
          "map": {
            "section": "data.section"
          }
        }
      }
    }
  }
}
Out
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.

Event
{
  "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"
  }
}
Mapping
{
  "silent": true,
  "settings": {
    "screen": {
      "map": {
        "name": "data.screen_name",
        "properties": {
          "map": {
            "build": "data.build"
          }
        }
      }
    }
  }
}
Out
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.

Event
{
  "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"
  }
}
Mapping
{
  "silent": true,
  "settings": {
    "identify": {
      "map": {
        "userId": "data.user_id",
        "traits": {
          "map": {
            "email": "data.email",
            "name": "data.name",
            "plan": "data.plan"
          }
        }
      }
    }
  }
}
Out
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.

💡 Need implementation support?
elbwalker offers hands-on support: setup review, measurement planning, destination mapping, and live troubleshooting. Book a 2-hour session (€399)