Octov0.4.2
ReferenceConnectors

Database

SQL databases: the database connector and sql block.

The database connector owns a SQL connection pool — Postgres or SQLite, selected by the driver setting — and the sql block runs statements through it, folding the result into the message body. Both drivers are pure Go, so no CGO toolchain is needed.

database connector

The connector opens the pool on start (verifying the connection so a bad DSN fails fast) and closes it on shutdown.

Provides a source: no — it is a service connector. Blocks that bind to it: sql.

Settings

SettingTypeRequiredDefaultDescription
driverenum: postgres | sqliteYesDatabase driver. postgres uses jackc/pgx; sqlite uses modernc.org/sqlite.
dsnstringYesData source name (Postgres connection string or SQLite file path).
passwordstringNoPassword merged into the DSN at startup, so only it has to be a secret. Postgres only.
maxOpenConnsintNounlimitedOpen connection pool cap (0 = unlimited).
maxIdleConnsintNodatabase/sql defaultIdle connections kept in the pool.
connMaxLifetimestringNoDuration bounding connection reuse (e.g. 5m).

Example DSNs:

# Postgres
dsn: postgres://user:pw@localhost:5432/orders?sslmode=disable
# SQLite (file path)
dsn: file:./orders.db

Keeping only the password secret

Put the password in password rather than inside the DSN, and the connection string stays plain config — host, user, database and sslmode are not secrets, and rotating the password touches one value instead of the whole DSN:

env:
  - name: DB_PASSWORD
    required: true

connectors:
  - name: db
    type: database
    settings:
      driver: postgres
      dsn: postgres://app@db.internal:5432/app?sslmode=require
      password: ${DB_PASSWORD}

Both Postgres DSN forms are supported — the URL form above and the keyword form (host=db.internal user=app dbname=app) — and the password is escaped or quoted for the form it is merged into, so it needs no hand-encoding. If the DSN already carries a password, the password setting overrides it and the connector logs a warning. password means nothing to SQLite, so setting it with driver: sqlite fails at startup rather than being silently ignored.

sql block

SQL — execute a SQL statement against a database connector. In query mode (the default) the body becomes an array of row objects, or a single object when single is set; in exec mode the body becomes {"rowsAffected": N}. Bind parameters come from CEL expressions evaluated against the message.

SettingTypeRequiredDefaultDescription
connectorstringYesName of the database connector to use.
querystringYesSQL statement with placeholders.
argslist of expressionNoCEL expressions bound to the statement placeholders, in order.
execbooleanNofalseUse exec mode (no result set); result becomes {rowsAffected: N}.
singlebooleanNofalseIn query mode, unwrap the result to the first row object (or null when no rows match).

Placeholder style

Placeholders are the driver's own:

  • Postgres — numbered: $1, $2, …
  • SQLite — positional: ?
# postgres
query: "SELECT * FROM orders WHERE id = $1"
# sqlite
query: "SELECT * FROM orders WHERE id = ?"

Result shape

Rows are scanned into column-name → value maps and normalized through JSON, so downstream blocks and CEL expressions see consistent decoded-JSON types (numbers are floats, text/blob columns are strings).

Example

Adapted from samples/db-orders.yaml — an HTTP API over SQLite:

service:
  name: db-orders

env:
  - name: DB_DRIVER
    default: sqlite
  - name: DB_DSN
    default: file:./orders.db

connectors:
  - name: api
    type: http
    settings:
      basePath: /api/v1
      requestTimeout: 5s
  - name: orders-db
    type: database
    settings:
      driver: ${DB_DRIVER}
      dsn: ${DB_DSN}
      maxOpenConns: 4

flows:
  # POST /orders — insert a row and return it (single: true -> one object).
  - name: orders
    source:
      connector: api
      type: http
      settings:
        path: /orders
    process:
      - type: sql
        name: insert-order
        settings:
          connector: orders-db
          query: "INSERT INTO orders (item, amount) VALUES (?, ?) RETURNING *"
          args:
            - body.item
            - body.amount
          single: true

  # GET /orders/{id} — the matching row, or null when absent.
  - name: orders-get
    source:
      connector: api
      type: http
      settings:
        path: /orders/{id}
    process:
      - type: sql
        name: select-order
        settings:
          connector: orders-db
          query: "SELECT * FROM orders WHERE id = ?"
          args:
            - vars.id
          single: true

Always pass user input through args — the arguments are bound as SQL parameters, never interpolated into the statement text.

On this page