pipo. docs

people-intake

Accept people from a webhook, normalise them and store them in SQLite.

people-intake.pipo#

# Runnable version of the docs/spec.md §4 example, limited to what the runner implements today.
#   PIPO_INTAKE_TOKEN=dev-token bun pipo run examples/people-intake/people-intake.pipo
#   curl -X POST localhost:8787/in/people-intake/people -H 'X-Pipo-Token: dev-token' \
#        -H 'content-type: application/json' -d '{"name": " Ada ", "age": 36}'
pipo: 1
name: people-intake
description: Accept people from a webhook, normalise them and store them in SQLite.

fn: ./people.fn.ts

secrets:
  intake_token: env:PIPO_INTAKE_TOKEN

errors:
  retry: 2
  backoff: exponential
  delay: 500ms
  then: dead_letter

input:
  via: http
  with:
    path: /people
    method: POST
    listen: 8787
    auth:
      header: X-Pipo-Token
      equals: "${secrets.intake_token}"
  format: json
  validate:
    - exists(data)
    - type(data.name) == "string" && len(trim(data.name)) > 0
    - data.age > 30
  on_invalid:
    respond: 422
    message: "Rejected: rule '${error.rule}' failed"

nodes:
  logger:
    label: Logger
    from: input
    tap: log
    with:
      level: info
      message: "Received ${meta.packet_id} via ${meta.trigger} from ${meta.source}"

  normalize:
    from: logger
    transform: fn.textTransformer

output:
  from: normalize
  to: sqlite
  with:
    path: ./data/people.db
    table: people
    create: true
    mode: upsert
    key: id
    columns:
      id: "${meta.packet_id}"
      name: "${data.name}"
      age: "${data.age}"
      bio: "${data.bio}"
      received_at: "${meta.received_at}"
  validate:
    - type(data.age) == "number"
    - len(data.name) <= 200
  on_invalid:
    then: dead_letter
    message: "Packet ${meta.packet_id} failed output rule '${error.rule}'; not written"
  on_error:
    retry: 5
    backoff: exponential
    max_delay: 1m
    then: dead_letter
    message: "Could not write ${meta.packet_id} to ${output.with.path}#${output.with.table} after ${error.attempts} attempts: ${error.message}"

delivered:
  check: record_exists
  with:
    where: {id: "${meta.packet_id}"}
  within: 10s
  on_fail:
    then: dead_letter
    message: >
      Delivery unverified: sqlite acknowledged packet ${meta.packet_id} (rowid ${result.rowid}),
      but no row with id=${meta.packet_id} exists in ${output.with.table} after ${error.elapsed}.

Source: examples/people-intake.