Skip to main content
Server Source code Package

Fetch

Web Standard Fetch API source for walkerOS. Platform-agnostic (Request) => Response signature that runs on Cloudflare Workers, Vercel Edge, Deno, Bun, and Node.js 18+. Supports batch processing, configurable CORS, and pixel tracking via a 1x1 transparent GIF for GET requests.

Where this fits

The Fetch source is a server source in the walkerOS flow:

It receives events via the Fetch API and forwards them to your destinations. Works on any platform supporting Web Standards.

Installation

npm install @walkeros/server-source-fetch
path setting renamed to paths

The path setting has been renamed to paths (array). The old path still works but is deprecated and will be removed in the next major version.

// Before (deprecated)
settings: { path: '/events' }

// After
settings: { paths: ['/events'] }

Cloudflare Workers

import { sourceFetch } from '@walkeros/server-source-fetch';
import { startFlow } from '@walkeros/collector';

const { sources } = await startFlow({
  sources: {
    fetch: {
      code: sourceFetch,
      config: {
        settings: { paths: ['/collect'], cors: true },
      },
    },
  },
});

export default { fetch: sources.fetch.push };

Configuration

This source uses the standard source config wrapper (consent, data, env, id, ...). For the shared fields see source configuration. Package-specific fields live under config.settings and are listed below.

Settings

PropertyTypeDescriptionMore
pathstringDeprecated: use paths instead
pathsArray<any>Route paths to handle. String shorthand accepts GET+POST. RouteConfig allows per-route method control.
corsboolean | objectCORS configuration: false = disabled, true = allow all (default), object = custom
maxRequestSizeintegerMaximum request body size in bytes
maxBatchSizeintegerMaximum events per batch request

Mapping

This package does not define custom rule-level settings. For the standard rule fields (consent, condition, data, batch, name, policy) see mapping.

Examples

Batch POST

A fetch POST with a batch array produces one walker elb event per batched item preserving order.

Event
{
  "method": "POST",
  "url": "http://localhost/collect",
  "body": {
    "batch": [
      {
        "name": "page view",
        "data": {
          "title": "Home"
        }
      },
      {
        "name": "button click",
        "data": {
          "id": "cta"
        }
      }
    ]
  }
}
Out
elb({
  "name": "page view",
  "data": {
    "title": "Home"
  }
});

elb({
  "name": "button click",
  "data": {
    "id": "cta"
  }
})

Pixel GET

A fetch GET with query parameters in the URL is parsed into an elb event payload for pixel-style tracking.

Event
{
  "method": "GET",
  "url": "http://localhost/collect?e=page+view&d=%7B%22title%22%3A%22Home%22%7D"
}
Out
elb({
  "e": "page view",
  "d": "{\"title\":\"Home\"}"
})

POST event

A fetch POST request with a JSON body becomes a single walker elb event in a fetch-based server.

Event
{
  "method": "POST",
  "url": "http://localhost/collect",
  "body": {
    "name": "page view",
    "data": {
      "title": "Docs",
      "url": "https://example.com/docs"
    }
  }
}
Out
elb({
  "name": "page view",
  "data": {
    "title": "Docs",
    "url": "https://example.com/docs"
  }
})

Responses

StatusMeaning
200Event processed
400Rejected client input: the pipeline declared the event invalid. The body echoes the validation message, for example Event name is required
500The pipeline failed to process a valid event, or an unexpected server fault
207Batch request where some events succeeded and others failed. The body carries per-index errors

Invalid input is counted on collector.status.sources.<id>.rejected rather than inflating status.failed, and is logged at warn with the reason instead of as an error with a stack trace.

Ingest metadata

Extract request metadata and forward it through the pipeline.

config.ingest must use the map operator. Keys are output field names; values are direct field paths on the Request (no req. prefix), or { fn } for header access via .get(). A bare object like { url: 'url' } is silently inert: without the map operator the source passes the whole request through and no field is extracted.

const { sources } = await startFlow({
  sources: {
    fetch: {
      code: sourceFetch,
      config: {
        settings: { cors: true },
        ingest: {
          map: {
            ua: { key: 'headers.user-agent' },
            origin: { key: 'headers.origin' },
            path: { key: 'path' },
          },
        },
      },
    },
  },
});

Available ingest paths

This source builds the shared request scope, so every contract field resolves by key.

PathDescription
methodUppercase HTTP method
urlAbsolute request URL
pathPathname, no query string
query.<name>Query parameter
headers.<name>Header, lowercased name
bodyParsed request body
rawThe WHATWG Request itself
note

ip is absent on this source: a Fetch Request reports no client IP and walkerOS does not guess one. Read headers.x-forwarded-for if your proxy sets it.

Usage

Single event

fetch('https://your-endpoint.com/collect', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    name: 'page view',
    data: { title: 'Home', path: '/' },
  }),
});

Batch events

fetch('https://your-endpoint.com/collect', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    batch: [
      { name: 'page view', data: { title: 'Home' } },
      { name: 'button click', data: { id: 'cta' } },
    ],
  }),
});
💡 Need implementation support?
elbwalker offers hands-on support: setup review, measurement planning, destination mapping, and live troubleshooting. Book a 2-hour session (€399)