Hookway

Variables — variables

# examples/variables.yaml
#
# Reference config for routing VARIABLES: per-request values derived from the
# request path, a filtered header, and the JSON body fill {name} placeholders
# in publish targets and in a webhook destination url.
#
# The scenario: one Hookway instance in front of several environments
# (qa-1, staging, prod, ...). The vendor is given one webhook URL per
# environment, e.g. https://hooks.example.com/webhooks/qa-1/vendor, and each
# event lands in that environment's pre-provisioned SQS queue and downstream
# API.
#
# Path placeholders on http_trigger (e.g. {envName}) are copied onto
# envelope.Vars automatically. Body fields can be copied with a transform
# field_map node. A code node can derive variables from filtered headers.
# Unresolved templates and unsafe variable values fail closed at publish.

server:
  addr: ":8080"
  max_body_bytes: 1048576

security:
  # The code node below can inspect this routing header, but not the inbound
  # authentication token or any other unlisted header.
  forwarded_header_allowlist: [Content-Type, X-Hookway-Environment]

destinations:
  environment_queues:
    type: sqs
    sqs:
      region: "us-east-1"
  downstream_api:
    type: webhook
    webhook:
      # The host is templated. A placeholder in the host must be followed by a
      # literal domain suffix so a request can never choose an arbitrary host.
      # The route target is appended as the final path segment.
      url: "https://{envName}-api.example.com/v1/events/webhook"
      timeout: "10s"
      auth:
        type: "api_key"
        header: "Authorization"
        value: "${DOWNSTREAM_API_KEY}"
  audit:
    type: mongodb
    mongodb:
      uri: "${MONGODB_URI}"
      database: "webhooks"

flows:
  vendor:
    nodes:
      - {id: in, type: http_trigger, path: "/webhooks/{envName}/vendor", methods: [POST]}
      - {id: body, type: read_body, max_bytes: 1048576}
      - {id: auth, type: auth, scheme: exact, secret: "${VENDOR_WEBHOOK_TOKEN}", signature_header: X-Vendor-Token}
      - {id: env, type: envelope, body_format: json}
      - id: route
        type: code
        timeout: 50ms
        description: "Use a filtered header when present; otherwise keep the environment from the URL path."
        script: |
          const allowed = ['qa-1', 'staging', 'prod'];
          const requested = input.headers['X-Hookway-Environment'] || input.vars.envName;
          if (allowed.indexOf(requested) === -1) throw new Error('unknown environment');
          return { body: input.body, vars: { envName: requested } };
      - id: map
        type: transform
        kind: field_map
        mappings:
          - from: body.data.object.organizationId
            to: vars.tenant
      # envName comes from the allowlisted header or path; tenant is filled by
      # field_map. The queue must already exist, so an unknown environment does
      # not create infrastructure. Unresolved templates fail closed at publish.
      # DynamoDB lookup variable envByApp is omitted until the lookup node grows
      # those fields (index, filter, value_attribute, region).
      - {id: out_queue, type: publish, destination: environment_queues, target: "{envName}-events"}
      - {id: out_api, type: publish, destination: downstream_api, target: event}
      - {id: out_audit, type: publish, destination: audit, target: "{envName}-{tenant}"}
    edges: [in>body, body>auth, auth>env, env>route, route>map, map>out_queue, map>out_api, map>out_audit]