Postgres
Declare PostgreSQL connections and table schemas for use in workflow nodes.
What it is. The PostgreSQL database you already have, declared with its table schemas so workflows can use it.
Use it when workflow steps should read or write your existing records with parameterized SQL.
Works with type: postgres nodes (the SQL steps) and secret blocks (the connection credentials). For Swirls-managed output storage, use a stream instead.
postgres blocks represent user-managed external PostgreSQL databases: databases you operate, outside Swirls platform-managed streams and type: stream nodes. After you declare a connection and table row schemas, type: postgres nodes can run parameterized SELECT (select:) or INSERT (insert:) SQL against those tables.
Declaring a postgres block
There is no type: field: the keyword postgres identifies the block.
| Field | Required | Description |
|---|---|---|
label | No | Human-readable label. |
secrets | No | References a top-level secret block; bare connection identifiers are validated against that block’s vars. |
connection | Yes | Connection string: a bare identifier (secret ref) or a quoted literal for testing (the validator warns on literals in production-oriented workflows). |
table <name> { } | Yes (≥1) | Each nested block declares a table name and a JSON Schema for one row. |
postgres my_db {
label: "CRM database"
secrets: project_secrets
connection: SHARED_DATABASE_URL
table leads {
schema: @json {
{
"type": "object",
"properties": {
"id": { "type": "string" },
"email": { "type": "string" },
"score": { "type": "number" }
},
"required": ["id", "email"]
}
}
}
}Table names in the DSL are unqualified (for example leads); runtime uses your database’s default schema unless you extend conventions later.
Table declarations and tooling
Each table block’s schema is JSON Schema for a single row. The language service uses it to:
- Validate that explicit
INSERT INTO t (col1, col2)column names exist on the declared table. - Infer TypeScript types for
paramsreturn objects and for{{key}}hovers from SQL (for example, which column a placeholder maps to).
Select nodes (select:)
Use exactly one of select: or insert: on a type: postgres node. For reads, use select: with a SELECT (or WITH) statement.
| Field | Required | Description |
|---|---|---|
postgres | Yes | Name of a top-level postgres block. |
select | Yes | @sql block: must read as a SELECT (or WITH) statement. |
params | Optional; required when the SQL contains {{key}} placeholders | @ts block returning a plain object whose keys match those placeholders. |
schema / outputSchema | Recommended | JSON Schema for the array of result rows. |
node load_leads {
type: postgres
label: "Active leads"
postgres: my_db
select: @sql {
SELECT id, email, score
FROM leads
WHERE score >= {{min_score}}
}
params: @ts {
return {
min_score: context.nodes.root.output.threshold
}
}
schema: @json {
{
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "string" },
"email": { "type": "string" },
"score": { "type": "number" }
}
}
}
}
}Node output is an array of row objects (query result rows).
Insert nodes (insert:)
For writes, use insert: with an INSERT statement. params: is required.
| Field | Required | Description |
|---|---|---|
postgres | Yes | Name of a top-level postgres block. |
insert | Yes | @sql block: an INSERT statement (including upsert forms your validator accepts). |
params | Yes | @ts block returning a plain object whose keys match {{key}} in the SQL. |
condition | No | @ts block returning a boolean; if false, the insert is skipped. |
Wrap row values in VALUES (...): the validator requires VALUES to be followed by parentheses around the value list.
node upsert_lead {
type: postgres
postgres: my_db
condition: @ts {
return context.nodes.classify.output.score > 20
}
insert: @sql {
INSERT INTO leads (name, email, score)
VALUES ({{name}}, {{email}}, {{score}})
}
params: @ts {
return {
name: context.nodes.root.output.name,
email: context.nodes.root.output.email,
score: context.nodes.classify.output.score
}
}
}Typical output shape is { executed: true } or, when condition is false, { skipped: true }.
Parameter interpolation ({{key}})
Values are not concatenated into the SQL string. At runtime, each {{key}} is replaced with a positional placeholder ($1, $2, …) and the corresponding value is sent as a bound parameter: so user data never becomes raw SQL text.
In the DSL, params: returns a named object. Placeholder names in SQL and keys in that object must match when placeholders exist. Those names do not need to match JSON Schema property names on the row: types are inferred from SQL position (for example, which column appears in INSERT INTO t (a,b) VALUES ({{x}},{{y}})) or from simple comparison patterns on SELECT (for example WHERE col = {{key}}).
Type safety in the editor
When table row schemas are declared and SQL is parseable by the tooling, you get:
- Hover types on
{{key}}inside@sqlblocks. - Type checking on the object returned from
params@tsblocks. - An index signature on the inferred params type so extra keys remain valid when you map names freely.
The Node types reference lists all postgres node fields in one place.