This workflow follows the Execute Workflow Trigger → Postgres recipe pattern — see all workflows that pair these two integrations.
The workflow JSON
Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →
{
"updatedAt": "2025-12-24T09:01:28.106Z",
"createdAt": "2025-12-23T09:30:27.014Z",
"id": "CgUAxK0i4YhrZ2Wp",
"name": "Execute_Queries",
"description": null,
"active": false,
"isArchived": false,
"nodes": [
{
"parameters": {
"inputSource": "passthrough"
},
"type": "n8n-nodes-base.executeWorkflowTrigger",
"typeVersion": 1.1,
"position": [
0,
0
],
"id": "trigger-execute-queries",
"name": "ExecuteWorkflowTrigger"
},
{
"parameters": {
"jsCode": "// Validate input and initialize loop state\n// Execute_Queries: Unified sub-workflow for all database operations\n// - Executes queries sequentially (supports chaining via $results)\n// - Preserves ctx throughout\n// - Stores results in ctx.db[key] = { row, rows, count }\n\nconst item = $input.first();\nconst ctx = item.json.ctx;\n\n// If no queries or empty array, return state that will skip the loop gracefully\nif (!ctx?.db_queries || !Array.isArray(ctx.db_queries) || ctx.db_queries.length === 0) {\n return [{\n json: {\n original_ctx: ctx || {},\n queries: [],\n current_index: 0,\n results: {},\n has_more: false\n }\n }];\n}\n\n// Validate each query has required fields\nfor (let i = 0; i < ctx.db_queries.length; i++) {\n const q = ctx.db_queries[i];\n if (!q.key) {\n throw new Error(`Execute_Queries: Query at index ${i} missing 'key' field`);\n }\n if (!q.sql) {\n throw new Error(`Execute_Queries: Query '${q.key}' at index ${i} missing 'sql' field`);\n }\n}\n\n// Initialize loop state\nreturn [{\n json: {\n original_ctx: ctx,\n queries: ctx.db_queries,\n current_index: 0,\n results: {},\n has_more: true\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
224,
0
],
"id": "initialize-loop",
"name": "InitializeLoop"
},
{
"parameters": {
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"conditions": [
{
"id": "has-more",
"leftValue": "={{ $json.has_more }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
336,
-96
],
"id": "has-queries",
"name": "HasQueries?"
},
{
"parameters": {
"jsCode": "// Prepare current query for execution\n// Supports $results.key.field references in params for chaining\n// Also formats arrays as PostgreSQL array literals when needed\nconst item = $input.first();\nconst state = item.json;\nconst query = state.queries[state.current_index];\n\n// Helper to format JS arrays as PostgreSQL array literals\n// Handles special characters: commas, braces, quotes, backslashes\nfunction formatPgArray(arr) {\n if (!Array.isArray(arr)) return arr;\n \n const formatted = arr.map(item => {\n if (Array.isArray(item)) {\n return formatPgArray(item); // Recursive for nested arrays\n }\n if (typeof item === 'string') {\n // Escape and quote strings with special characters\n if (item.includes(',') || item.includes('{') || item.includes('}') || \n item.includes('\"') || item.includes('\\\\') || item.includes(' ')) {\n return '\"' + item.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"') + '\"';\n }\n }\n return item;\n });\n \n return '{' + formatted.join(',') + '}';\n}\n\n// Process params to resolve $results references\nlet params = query.params || [];\n\nif (params.length > 0) {\n params = params.map(param => {\n if (typeof param === 'string' && param.startsWith('$results.')) {\n // Parse reference like \"$results.trace.row.id\"\n const path = param.substring('$results.'.length).split('.');\n let value = state.results;\n for (const key of path) {\n if (value && typeof value === 'object') {\n value = value[key];\n } else {\n value = undefined;\n break;\n }\n }\n if (value === undefined) {\n throw new Error(`Execute_Queries: Could not resolve reference ${param}`);\n }\n // Auto-format arrays for PostgreSQL\n return formatPgArray(value);\n }\n // Also format arrays that aren't references\n if (Array.isArray(param)) {\n return formatPgArray(param);\n }\n return param;\n });\n}\n\nreturn [{\n json: {\n ...state,\n current_query: query,\n sql: query.sql,\n params: params\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
448,
0
],
"id": "prepare-query",
"name": "PrepareQuery"
},
{
"parameters": {
"operation": "executeQuery",
"query": "{{ $json.sql }}",
"options": {
"queryReplacement": "={{ $json.params }}"
}
},
"type": "n8n-nodes-base.postgres",
"typeVersion": 2.4,
"position": [
672,
0
],
"id": "execute-postgres-query",
"name": "ExecuteQuery",
"alwaysOutputData": true,
"credentials": {
"postgres": {
"name": "<your credential>"
}
}
},
{
"parameters": {
"mode": "append",
"numberInputs": 2
},
"type": "n8n-nodes-base.merge",
"typeVersion": 3,
"position": [
880,
0
],
"id": "merge-results",
"name": "MergeResults"
},
{
"parameters": {
"jsCode": "// Collect query results and check if more queries remain\nconst items = $input.all();\n\n// Find state from merge (has original_ctx)\nlet state = null;\nconst queryResults = [];\n\nfor (const item of items) {\n if (item.json.original_ctx) {\n state = item.json;\n } else {\n // This is a Postgres result row\n queryResults.push(item.json);\n }\n}\n\nif (!state) {\n throw new Error('Execute_Queries: Lost loop state');\n}\n\n// Store results for current query\nconst currentKey = state.current_query.key;\nstate.results[currentKey] = {\n row: queryResults[0] || null, // First row (common for RETURNING or single-row SELECT)\n rows: queryResults, // All rows\n count: queryResults.length // Row count\n};\n\n// Move to next query\nstate.current_index++;\n\n// Check if more queries remain\nconst hasMore = state.current_index < state.queries.length;\n\nreturn [{\n json: {\n ...state,\n has_more: hasMore\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1104,
0
],
"id": "collect-results",
"name": "CollectResults"
},
{
"parameters": {
"conditions": {
"options": {
"version": 2,
"leftValue": "",
"caseSensitive": true,
"typeValidation": "loose"
},
"conditions": [
{
"id": "has-more",
"leftValue": "={{ $json.has_more }}",
"rightValue": true,
"operator": {
"type": "boolean",
"operation": "equals"
}
}
],
"combinator": "and"
},
"options": {}
},
"type": "n8n-nodes-base.if",
"typeVersion": 2.2,
"position": [
1328,
0
],
"id": "check-more-queries",
"name": "MoreQueries?"
},
{
"parameters": {
"jsCode": "// Restore ctx with all query results in ctx.db namespace\nconst item = $input.first();\nconst state = item.json;\n\n// Remove db_queries since it was consumed\nconst { db_queries, ...restCtx } = state.original_ctx;\n\nreturn [{\n json: {\n ctx: {\n ...restCtx,\n db: {\n ...(restCtx.db || {}),\n ...state.results\n }\n }\n }\n}];"
},
"type": "n8n-nodes-base.code",
"typeVersion": 2,
"position": [
1552,
112
],
"id": "finalize-context",
"name": "FinalizeContext"
}
],
"connections": {
"ExecuteWorkflowTrigger": {
"main": [
[
{
"node": "InitializeLoop",
"type": "main",
"index": 0
}
]
]
},
"InitializeLoop": {
"main": [
[
{
"node": "HasQueries?",
"type": "main",
"index": 0
}
]
]
},
"HasQueries?": {
"main": [
[
{
"node": "PrepareQuery",
"type": "main",
"index": 0
}
],
[
{
"node": "FinalizeContext",
"type": "main",
"index": 0
}
]
]
},
"PrepareQuery": {
"main": [
[
{
"node": "ExecuteQuery",
"type": "main",
"index": 0
},
{
"node": "MergeResults",
"type": "main",
"index": 1
}
]
]
},
"ExecuteQuery": {
"main": [
[
{
"node": "MergeResults",
"type": "main",
"index": 0
}
]
]
},
"MergeResults": {
"main": [
[
{
"node": "CollectResults",
"type": "main",
"index": 0
}
]
]
},
"CollectResults": {
"main": [
[
{
"node": "MoreQueries?",
"type": "main",
"index": 0
}
]
]
},
"MoreQueries?": {
"main": [
[
{
"node": "PrepareQuery",
"type": "main",
"index": 0
}
],
[
{
"node": "FinalizeContext",
"type": "main",
"index": 0
}
]
]
}
},
"settings": {
"executionOrder": "v1",
"callerPolicy": "workflowsFromSameOwner",
"availableInMCP": false
},
"staticData": null,
"meta": null,
"versionId": "7ed2d22e-978a-4d88-b66d-909616bed0e6",
"activeVersionId": null,
"versionCounter": 137,
"triggerCount": 0,
"shared": [
{
"updatedAt": "2025-12-23T09:30:27.014Z",
"createdAt": "2025-12-23T09:30:27.014Z",
"role": "workflow:owner",
"workflowId": "CgUAxK0i4YhrZ2Wp",
"projectId": "erM3nntdLL53noWi",
"project": {
"updatedAt": "2025-12-23T09:23:39.658Z",
"createdAt": "2025-12-23T09:16:56.460Z",
"id": "erM3nntdLL53noWi",
"name": "Chris Irineo <chriskevini@gmail.com>",
"type": "personal",
"icon": null,
"description": null,
"projectRelations": [
{
"updatedAt": "2025-12-23T09:16:56.460Z",
"createdAt": "2025-12-23T09:16:56.460Z",
"userId": "2a851a2d-b7e5-4b3c-aefb-6eaaa79e0659",
"projectId": "erM3nntdLL53noWi",
"user": {
"updatedAt": "2025-12-24T08:40:46.063Z",
"createdAt": "2025-12-23T09:16:54.881Z",
"id": "2a851a2d-b7e5-4b3c-aefb-6eaaa79e0659",
"email": "chriskevini@gmail.com",
"firstName": "Chris",
"lastName": "Irineo",
"personalizationAnswers": {
"version": "v4",
"personalization_survey_submitted_at": "2025-12-23T09:23:43.723Z",
"personalization_survey_n8n_version": "1.123.5"
},
"settings": {
"userActivated": true,
"firstSuccessfulWorkflowId": "CgUAxK0i4YhrZ2Wp",
"userActivatedAt": 1766487000077,
"easyAIWorkflowOnboarded": true
},
"disabled": false,
"mfaEnabled": false,
"lastActiveAt": "2025-12-24",
"isPending": false
}
}
]
}
}
],
"tags": [],
"activeVersion": null
}
Credentials you'll need
Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.
postgres
For the full experience including quality scoring and batch install features for each workflow upgrade to Pro
About this workflow
Execute_Queries. Uses executeWorkflowTrigger, postgres. Event-driven trigger; 9 nodes.
Source: https://github.com/chriskevini/kairon/blob/ab924f228ceb22522b9a4dfa1ab4589eb86273ad/n8n-workflows/Execute_Queries.json — original creator credit. Request a take-down →
Related workflows
Workflows that share integrations, category, or trigger type with this one. All free to copy and import.
Reagendamiento_v2. Uses executeWorkflowTrigger, redis, httpRequest, n8n-nodes-evolution-api. Event-driven trigger; 89 nodes.
Agendamiento_v2. Uses n8n-nodes-evolution-api, redis, httpRequest, executeWorkflowTrigger. Event-driven trigger; 59 nodes.
Cancelacion_v2. Uses executeWorkflowTrigger, redis, httpRequest, n8n-nodes-evolution-api. Event-driven trigger; 46 nodes.
Save_Extraction. Uses executeWorkflowTrigger, postgres, httpRequest. Event-driven trigger; 22 nodes.
Youtube Searcher. Uses splitInBatches, httpRequest, manualTrigger, executeWorkflowTrigger. Event-driven trigger; 21 nodes.