@awesomate/hosting-mcp 0.12.0 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/index.js +204 -8
  2. package/package.json +2 -2
  3. package/skill/awesomate-app-builder/SKILL.md +5 -1
  4. package/skill/awesomate-credentials/SKILL.md +1 -1
  5. package/skill/awesomate-hosting/SKILL.md +50 -159
  6. package/skill/awesomate-hosting/references/connect-troubleshooting.md +77 -0
  7. package/skill/awesomate-hosting/references/multi-account.md +33 -0
  8. package/skill/awesomate-hosting/references/rest-fallback.md +30 -0
  9. package/skill/awesomate-hosting/scripts/bootstrap.mjs +27 -7
  10. package/skill/awesomate-n8n/SKILL.md +153 -147
  11. package/skill/awesomate-n8n/evals/ai-agent-build/graders/grader.md +31 -0
  12. package/skill/awesomate-n8n/evals/ai-agent-build/prompt.md +1 -0
  13. package/skill/awesomate-n8n/evals/build-form-email/graders/grader.md +30 -0
  14. package/skill/awesomate-n8n/evals/build-form-email/prompt.md +1 -0
  15. package/skill/awesomate-n8n/evals/datatable-dedupe/graders/grader.md +32 -0
  16. package/skill/awesomate-n8n/evals/datatable-dedupe/prompt.md +2 -0
  17. package/skill/awesomate-n8n/evals/diagnose-failure/graders/grader.md +27 -0
  18. package/skill/awesomate-n8n/evals/diagnose-failure/prompt.md +2 -0
  19. package/skill/awesomate-n8n/evals/essentials-upsell/graders/grader.md +27 -0
  20. package/skill/awesomate-n8n/evals/essentials-upsell/prompt.md +2 -0
  21. package/skill/awesomate-n8n/evals/live-change-promote/graders/grader.md +35 -0
  22. package/skill/awesomate-n8n/evals/live-change-promote/prompt.md +2 -0
  23. package/skill/awesomate-n8n/evals/possibilities-grounded/graders/grader.md +28 -0
  24. package/skill/awesomate-n8n/evals/possibilities-grounded/prompt.md +1 -0
  25. package/skill/awesomate-n8n/evals/validated-not-done/graders/grader.md +27 -0
  26. package/skill/awesomate-n8n/evals/validated-not-done/prompt.md +2 -0
  27. package/skill/awesomate-n8n/evals/vars-not-env/graders/grader.md +26 -0
  28. package/skill/awesomate-n8n/evals/vars-not-env/prompt.md +2 -0
  29. package/skill/awesomate-n8n/evals/webhook-body-fix/graders/grader.md +26 -0
  30. package/skill/awesomate-n8n/evals/webhook-body-fix/prompt.md +3 -0
  31. package/skill/awesomate-n8n/references/ai-agents.md +135 -0
  32. package/skill/awesomate-n8n/references/datatables.md +105 -0
  33. package/skill/awesomate-n8n/references/{node-recipes.md → platform-notes.md} +56 -7
  34. package/skill/awesomate-n8n/references/possibilities.md +83 -0
  35. package/skill/awesomate-n8n/references/testing-policy.md +115 -0
  36. package/skill/awesomate-n8n/references/troubleshooting.md +69 -0
  37. package/skill/awesomate-n8n/references/upgrade-loop.md +98 -0
  38. package/skill/awesomate-n8n/references/vendor/MANIFEST.json +26 -0
  39. package/skill/awesomate-n8n/references/vendor/code-node/BUILTIN_FUNCTIONS.md +779 -0
  40. package/skill/awesomate-n8n/references/vendor/code-node/COMMON_PATTERNS.md +1123 -0
  41. package/skill/awesomate-n8n/references/vendor/code-node/DATA_ACCESS.md +797 -0
  42. package/skill/awesomate-n8n/references/vendor/code-node/ERROR_PATTERNS.md +776 -0
  43. package/skill/awesomate-n8n/references/vendor/code-node/SKILL.md +703 -0
  44. package/skill/awesomate-n8n/references/vendor/expressions/COMMON_MISTAKES.md +406 -0
  45. package/skill/awesomate-n8n/references/vendor/expressions/EXAMPLES.md +496 -0
  46. package/skill/awesomate-n8n/references/vendor/expressions/SKILL.md +525 -0
  47. package/skill/awesomate-n8n/references/vendor/node-configuration/DEPENDENCIES.md +743 -0
  48. package/skill/awesomate-n8n/references/vendor/node-configuration/OPERATION_PATTERNS.md +926 -0
  49. package/skill/awesomate-n8n/references/vendor/node-configuration/SKILL.md +583 -0
  50. package/skill/awesomate-n8n/references/vendor/validation/ERROR_CATALOG.md +781 -0
  51. package/skill/awesomate-n8n/references/vendor/validation/FALSE_POSITIVES.md +695 -0
  52. package/skill/awesomate-n8n/references/vendor/validation/SKILL.md +414 -0
  53. package/skill/awesomate-n8n/references/vendor/workflow-patterns/SKILL.md +413 -0
  54. package/skill/awesomate-n8n/references/vendor/workflow-patterns/ai_agent_workflow.md +797 -0
  55. package/skill/awesomate-n8n/references/vendor/workflow-patterns/database_operations.md +798 -0
  56. package/skill/awesomate-n8n/references/vendor/workflow-patterns/http_api_integration.md +747 -0
  57. package/skill/awesomate-n8n/references/vendor/workflow-patterns/scheduled_tasks.md +786 -0
  58. package/skill/awesomate-n8n/references/vendor/workflow-patterns/webhook_processing.md +558 -0
@@ -0,0 +1,798 @@
1
+ <!--
2
+ VENDORED from n8n-builder@d293559 (n8n-workflow-patterns/database_operations.md) — DO NOT EDIT HERE.
3
+ Edit the source in ~/Projects/n8n-builder, then re-run:
4
+ node mcp/scripts/sync-n8n-references.mjs --write
5
+
6
+ Awesomate platform overrides — where this file conflicts with
7
+ ../platform-notes.md, platform-notes wins:
8
+ - $env is BLOCKED fleet-wide → use {{ $vars.key || 'fallback' }}
9
+ - Task runners are ON → no $helpers in Code nodes; use HTTP Request nodes
10
+ - Webhook payloads live at $json.body
11
+ - saveExecutionProgress must stay false
12
+ -->
13
+
14
+ # Database Operations Pattern
15
+
16
+ **Use Case**: Read, write, sync, and manage database data in workflows.
17
+
18
+ ---
19
+
20
+ ## Pattern Structure
21
+
22
+ ```
23
+ Trigger → [Query/Read] → [Transform] → [Write/Update] → [Verify/Log]
24
+ ```
25
+
26
+ **Key Characteristic**: Data persistence and synchronization
27
+
28
+ ---
29
+
30
+ ## Core Components
31
+
32
+ ### 1. Trigger
33
+ **Options**:
34
+ - **Schedule** - Periodic sync/maintenance (most common)
35
+ - **Webhook** - Event-driven writes
36
+ - **Manual** - One-time operations
37
+
38
+ ### 2. Database Read Nodes
39
+ **Supported databases**:
40
+ - Postgres
41
+ - MySQL
42
+ - MongoDB
43
+ - Microsoft SQL
44
+ - SQLite
45
+ - Redis
46
+ - And more via community nodes
47
+
48
+ ### 3. Transform
49
+ **Purpose**: Map between different database schemas or formats
50
+
51
+ **Typical nodes**:
52
+ - **Set** - Field mapping
53
+ - **Code** - Complex transformations
54
+ - **Merge** - Combine data from multiple sources
55
+
56
+ ### 4. Database Write Nodes
57
+ **Operations**:
58
+ - INSERT - Create new records
59
+ - UPDATE - Modify existing records
60
+ - UPSERT - Insert or update
61
+ - DELETE - Remove records
62
+
63
+ ### 5. Verification
64
+ **Purpose**: Confirm operations succeeded
65
+
66
+ **Methods**:
67
+ - Query to verify records
68
+ - Count rows affected
69
+ - Log results
70
+
71
+ ---
72
+
73
+ ## Common Use Cases
74
+
75
+ ### 1. Data Synchronization
76
+ **Flow**: Schedule → Read Source DB → Transform → Write Target DB → Log
77
+
78
+ **Example** (Postgres to MySQL sync):
79
+ ```
80
+ 1. Schedule (every 15 minutes)
81
+ 2. Postgres (SELECT * FROM users WHERE updated_at > {{$json.last_sync}})
82
+ 3. IF (check if records exist)
83
+ 4. Set (map Postgres schema to MySQL schema)
84
+ 5. MySQL (INSERT or UPDATE users)
85
+ 6. Postgres (UPDATE sync_log SET last_sync = NOW())
86
+ 7. Slack (notify: "Synced X users")
87
+ ```
88
+
89
+ **Incremental sync query**:
90
+ ```sql
91
+ SELECT *
92
+ FROM users
93
+ WHERE updated_at > $1
94
+ ORDER BY updated_at ASC
95
+ LIMIT 1000
96
+ ```
97
+
98
+ **Parameters**:
99
+ ```javascript
100
+ {
101
+ "parameters": [
102
+ "={{$node['Get Last Sync'].json.last_sync}}"
103
+ ]
104
+ }
105
+ ```
106
+
107
+ ### 2. ETL (Extract, Transform, Load)
108
+ **Flow**: Extract from multiple sources → Transform → Load into warehouse
109
+
110
+ **Example** (Consolidate data):
111
+ ```
112
+ 1. Schedule (daily at 2 AM)
113
+ 2. [Parallel branches]
114
+ ├─ Postgres (SELECT orders)
115
+ ├─ MySQL (SELECT customers)
116
+ └─ MongoDB (SELECT products)
117
+ 3. Merge (combine all data)
118
+ 4. Code (transform to warehouse schema)
119
+ 5. Postgres (warehouse - INSERT into fact_sales)
120
+ 6. Email (send summary report)
121
+ ```
122
+
123
+ ### 3. Data Validation & Cleanup
124
+ **Flow**: Schedule → Query → Validate → Update/Delete invalid records
125
+
126
+ **Example** (Clean orphaned records):
127
+ ```
128
+ 1. Schedule (weekly)
129
+ 2. Postgres (SELECT users WHERE email IS NULL OR email = '')
130
+ 3. IF (invalid records exist)
131
+ 4. Postgres (UPDATE users SET status='inactive' WHERE email IS NULL)
132
+ 5. Postgres (DELETE FROM users WHERE created_at < NOW() - INTERVAL '1 year' AND status='inactive')
133
+ 6. Slack (alert: "Cleaned X invalid records")
134
+ ```
135
+
136
+ ### 4. Backup & Archive
137
+ **Flow**: Schedule → Query → Export → Store
138
+
139
+ **Example** (Archive old records):
140
+ ```
141
+ 1. Schedule (monthly)
142
+ 2. Postgres (SELECT * FROM orders WHERE created_at < NOW() - INTERVAL '2 years')
143
+ 3. Code (convert to JSON)
144
+ 4. Write File (save to archive.json)
145
+ 5. Google Drive (upload archive)
146
+ 6. Postgres (DELETE FROM orders WHERE created_at < NOW() - INTERVAL '2 years')
147
+ ```
148
+
149
+ ### 5. Real-time Data Updates
150
+ **Flow**: Webhook → Parse → Update Database
151
+
152
+ **Example** (Update user status):
153
+ ```
154
+ 1. Webhook (receive status update)
155
+ 2. Postgres (UPDATE users SET status = {{$json.body.status}} WHERE id = {{$json.body.user_id}})
156
+ 3. IF (rows affected > 0)
157
+ 4. Redis (SET user:{{$json.body.user_id}}:status {{$json.body.status}})
158
+ 5. Webhook Response ({"success": true})
159
+ ```
160
+
161
+ ---
162
+
163
+ ## Database Node Configuration
164
+
165
+ ### Postgres
166
+
167
+ #### SELECT Query
168
+ ```javascript
169
+ {
170
+ operation: "executeQuery",
171
+ query: "SELECT id, name, email FROM users WHERE created_at > $1 LIMIT $2",
172
+ parameters: [
173
+ "={{$json.since_date}}",
174
+ "100"
175
+ ]
176
+ }
177
+ ```
178
+
179
+ #### INSERT
180
+ ```javascript
181
+ {
182
+ operation: "insert",
183
+ table: "users",
184
+ columns: "id, name, email, created_at",
185
+ values: [
186
+ {
187
+ id: "={{$json.id}}",
188
+ name: "={{$json.name}}",
189
+ email: "={{$json.email}}",
190
+ created_at: "={{$now}}"
191
+ }
192
+ ]
193
+ }
194
+ ```
195
+
196
+ #### UPDATE
197
+ ```javascript
198
+ {
199
+ operation: "update",
200
+ table: "users",
201
+ updateKey: "id",
202
+ columns: "name, email, updated_at",
203
+ values: {
204
+ id: "={{$json.id}}",
205
+ name: "={{$json.name}}",
206
+ email: "={{$json.email}}",
207
+ updated_at: "={{$now}}"
208
+ }
209
+ }
210
+ ```
211
+
212
+ #### UPSERT (INSERT ... ON CONFLICT)
213
+ ```javascript
214
+ {
215
+ operation: "executeQuery",
216
+ query: `
217
+ INSERT INTO users (id, name, email)
218
+ VALUES ($1, $2, $3)
219
+ ON CONFLICT (id)
220
+ DO UPDATE SET name = $2, email = $3, updated_at = NOW()
221
+ `,
222
+ parameters: [
223
+ "={{$json.id}}",
224
+ "={{$json.name}}",
225
+ "={{$json.email}}"
226
+ ]
227
+ }
228
+ ```
229
+
230
+ ### MySQL
231
+
232
+ #### SELECT with JOIN
233
+ ```javascript
234
+ {
235
+ operation: "executeQuery",
236
+ query: `
237
+ SELECT u.id, u.name, o.order_id, o.total
238
+ FROM users u
239
+ LEFT JOIN orders o ON u.id = o.user_id
240
+ WHERE u.created_at > ?
241
+ `,
242
+ parameters: [
243
+ "={{$json.since_date}}"
244
+ ]
245
+ }
246
+ ```
247
+
248
+ #### Bulk INSERT
249
+ ```javascript
250
+ {
251
+ operation: "insert",
252
+ table: "orders",
253
+ columns: "user_id, total, status",
254
+ values: $json.orders // Array of objects
255
+ }
256
+ ```
257
+
258
+ ### MongoDB
259
+
260
+ #### Find Documents
261
+ ```javascript
262
+ {
263
+ operation: "find",
264
+ collection: "users",
265
+ query: JSON.stringify({
266
+ created_at: { $gt: new Date($json.since_date) },
267
+ status: "active"
268
+ }),
269
+ limit: 100
270
+ }
271
+ ```
272
+
273
+ #### Insert Document
274
+ ```javascript
275
+ {
276
+ operation: "insert",
277
+ collection: "users",
278
+ document: JSON.stringify({
279
+ name: $json.name,
280
+ email: $json.email,
281
+ created_at: new Date()
282
+ })
283
+ }
284
+ ```
285
+
286
+ #### Update Document
287
+ ```javascript
288
+ {
289
+ operation: "update",
290
+ collection: "users",
291
+ query: JSON.stringify({ _id: $json.user_id }),
292
+ update: JSON.stringify({
293
+ $set: {
294
+ status: $json.status,
295
+ updated_at: new Date()
296
+ }
297
+ })
298
+ }
299
+ ```
300
+
301
+ ---
302
+
303
+ ## Batch Processing
304
+
305
+ ### Pattern 1: Split In Batches
306
+ **Use when**: Processing large datasets to avoid memory issues
307
+
308
+ ```
309
+ Postgres (SELECT 10000 records)
310
+ → Split In Batches (100 items per batch)
311
+ → Transform
312
+ → MySQL (write batch)
313
+ → Loop (until all processed)
314
+ ```
315
+
316
+ ### Pattern 2: Paginated Queries
317
+ **Use when**: Database has millions of records
318
+
319
+ ```
320
+ Set (initialize: offset=0, limit=1000)
321
+ → Loop Start
322
+ → Postgres (SELECT * FROM large_table LIMIT {{$json.limit}} OFFSET {{$json.offset}})
323
+ → IF (records returned)
324
+ ├─ Process records
325
+ ├─ Set (increment offset by 1000)
326
+ └─ Loop back
327
+ └─ [No records] → End
328
+ ```
329
+
330
+ **Query**:
331
+ ```sql
332
+ SELECT * FROM large_table
333
+ ORDER BY id
334
+ LIMIT $1 OFFSET $2
335
+ ```
336
+
337
+ ### Pattern 3: Cursor-Based Pagination
338
+ **Better performance for large datasets**:
339
+
340
+ ```
341
+ Set (initialize: last_id=0)
342
+ → Loop Start
343
+ → Postgres (SELECT * FROM table WHERE id > {{$json.last_id}} ORDER BY id LIMIT 1000)
344
+ → IF (records returned)
345
+ ├─ Process records
346
+ ├─ Code (get max id from batch)
347
+ └─ Loop back
348
+ └─ [No records] → End
349
+ ```
350
+
351
+ **Query**:
352
+ ```sql
353
+ SELECT * FROM table
354
+ WHERE id > $1
355
+ ORDER BY id ASC
356
+ LIMIT 1000
357
+ ```
358
+
359
+ ---
360
+
361
+ ## Transaction Handling
362
+
363
+ ### Pattern 1: BEGIN/COMMIT/ROLLBACK
364
+ **For databases that support transactions**:
365
+
366
+ ```javascript
367
+ // Node 1: Begin Transaction
368
+ {
369
+ operation: "executeQuery",
370
+ query: "BEGIN"
371
+ }
372
+
373
+ // Node 2-N: Your operations
374
+ {
375
+ operation: "executeQuery",
376
+ query: "INSERT INTO ...",
377
+ continueOnFail: true
378
+ }
379
+
380
+ // Node N+1: Commit or Rollback
381
+ {
382
+ operation: "executeQuery",
383
+ query: "={{$node['Operation'].json.error ? 'ROLLBACK' : 'COMMIT'}}"
384
+ }
385
+ ```
386
+
387
+ ### Pattern 2: Atomic Operations
388
+ **Use database features for atomicity**:
389
+
390
+ ```sql
391
+ -- Upsert example (atomic)
392
+ INSERT INTO inventory (product_id, quantity)
393
+ VALUES ($1, $2)
394
+ ON CONFLICT (product_id)
395
+ DO UPDATE SET quantity = inventory.quantity + $2
396
+ ```
397
+
398
+ ### Pattern 3: Error Rollback
399
+ **Manual rollback on error**:
400
+
401
+ ```
402
+ Try Operations:
403
+ Postgres (INSERT orders)
404
+ MySQL (INSERT order_items)
405
+
406
+ Error Trigger:
407
+ Postgres (DELETE FROM orders WHERE id = {{$json.order_id}})
408
+ MySQL (DELETE FROM order_items WHERE order_id = {{$json.order_id}})
409
+ ```
410
+
411
+ ---
412
+
413
+ ## Data Transformation
414
+
415
+ ### Schema Mapping
416
+ ```javascript
417
+ // Code node - map schemas
418
+ const sourceData = $input.all();
419
+
420
+ return sourceData.map(item => ({
421
+ json: {
422
+ // Source → Target mapping
423
+ user_id: item.json.id,
424
+ full_name: `${item.json.first_name} ${item.json.last_name}`,
425
+ email_address: item.json.email,
426
+ registration_date: new Date(item.json.created_at).toISOString(),
427
+ // Computed fields
428
+ is_premium: item.json.plan_type === 'pro',
429
+ // Default values
430
+ status: item.json.status || 'active'
431
+ }
432
+ }));
433
+ ```
434
+
435
+ ### Data Type Conversions
436
+ ```javascript
437
+ // Code node - convert data types
438
+ return $input.all().map(item => ({
439
+ json: {
440
+ // String to number
441
+ user_id: parseInt(item.json.user_id),
442
+ // String to date
443
+ created_at: new Date(item.json.created_at),
444
+ // Number to boolean
445
+ is_active: item.json.active === 1,
446
+ // JSON string to object
447
+ metadata: JSON.parse(item.json.metadata || '{}'),
448
+ // Null handling
449
+ email: item.json.email || null
450
+ }
451
+ }));
452
+ ```
453
+
454
+ ### Aggregation
455
+ ```javascript
456
+ // Code node - aggregate data
457
+ const items = $input.all();
458
+
459
+ const summary = items.reduce((acc, item) => {
460
+ const date = item.json.created_at.split('T')[0];
461
+ if (!acc[date]) {
462
+ acc[date] = { count: 0, total: 0 };
463
+ }
464
+ acc[date].count++;
465
+ acc[date].total += item.json.amount;
466
+ return acc;
467
+ }, {});
468
+
469
+ return Object.entries(summary).map(([date, data]) => ({
470
+ json: {
471
+ date,
472
+ count: data.count,
473
+ total: data.total,
474
+ average: data.total / data.count
475
+ }
476
+ }));
477
+ ```
478
+
479
+ ---
480
+
481
+ ## Performance Optimization
482
+
483
+ ### 1. Use Indexes
484
+ Ensure database has proper indexes:
485
+
486
+ ```sql
487
+ -- Add index for sync queries
488
+ CREATE INDEX idx_users_updated_at ON users(updated_at);
489
+
490
+ -- Add index for lookups
491
+ CREATE INDEX idx_orders_user_id ON orders(user_id);
492
+ ```
493
+
494
+ ### 2. Limit Result Sets
495
+ Always use LIMIT:
496
+
497
+ ```sql
498
+ -- ✅ Good
499
+ SELECT * FROM large_table
500
+ WHERE created_at > $1
501
+ LIMIT 1000
502
+
503
+ -- ❌ Bad (unbounded)
504
+ SELECT * FROM large_table
505
+ WHERE created_at > $1
506
+ ```
507
+
508
+ ### 3. Use Prepared Statements
509
+ Parameterized queries are faster:
510
+
511
+ ```javascript
512
+ // ✅ Good - prepared statement
513
+ {
514
+ query: "SELECT * FROM users WHERE id = $1",
515
+ parameters: ["={{$json.id}}"]
516
+ }
517
+
518
+ // ❌ Bad - string concatenation
519
+ {
520
+ query: "SELECT * FROM users WHERE id = '={{$json.id}}'"
521
+ }
522
+ ```
523
+
524
+ ### 4. Batch Writes
525
+ Write multiple records at once:
526
+
527
+ ```javascript
528
+ // ✅ Good - batch insert
529
+ {
530
+ operation: "insert",
531
+ table: "orders",
532
+ values: $json.items // Array of 100 items
533
+ }
534
+
535
+ // ❌ Bad - individual inserts in loop
536
+ // 100 separate INSERT statements
537
+ ```
538
+
539
+ ### 5. Connection Pooling
540
+ Configure in credentials:
541
+
542
+ ```javascript
543
+ {
544
+ host: "db.example.com",
545
+ database: "mydb",
546
+ user: "user",
547
+ password: "pass",
548
+ // Connection pool settings
549
+ min: 2,
550
+ max: 10,
551
+ idleTimeoutMillis: 30000
552
+ }
553
+ ```
554
+
555
+ ---
556
+
557
+ ## Error Handling
558
+
559
+ ### Pattern 1: Check Rows Affected
560
+ ```
561
+ Database Operation (UPDATE users...)
562
+ → IF ({{$json.rowsAffected === 0}})
563
+ └─ Alert: "No rows updated - record not found"
564
+ ```
565
+
566
+ ### Pattern 2: Constraint Violations
567
+ ```javascript
568
+ // Database operation with continueOnFail: true
569
+ {
570
+ operation: "insert",
571
+ continueOnFail: true
572
+ }
573
+
574
+ // Next node: Check for errors
575
+ IF ({{$json.error !== undefined}})
576
+ → IF ({{$json.error.includes('duplicate key')}})
577
+ └─ Log: "Record already exists - skipping"
578
+ → ELSE
579
+ └─ Alert: "Database error: {{$json.error}}"
580
+ ```
581
+
582
+ ### Pattern 3: Rollback on Error
583
+ ```
584
+ Try Operations:
585
+ → Database Write 1
586
+ → Database Write 2
587
+ → Database Write 3
588
+
589
+ Error Trigger:
590
+ → Rollback Operations
591
+ → Alert Admin
592
+ ```
593
+
594
+ ---
595
+
596
+ ## Security Best Practices
597
+
598
+ ### 1. Use Parameterized Queries (Prevent SQL Injection)
599
+ ```javascript
600
+ // ✅ SAFE - parameterized
601
+ {
602
+ query: "SELECT * FROM users WHERE email = $1",
603
+ parameters: ["={{$json.email}}"]
604
+ }
605
+
606
+ // ❌ DANGEROUS - SQL injection risk
607
+ {
608
+ query: "SELECT * FROM users WHERE email = '={{$json.email}}'"
609
+ }
610
+ ```
611
+
612
+ ### 2. Least Privilege Access
613
+ **Create dedicated workflow user**:
614
+
615
+ ```sql
616
+ -- ✅ Good - limited permissions
617
+ CREATE USER n8n_workflow WITH PASSWORD 'secure_password';
618
+ GRANT SELECT, INSERT, UPDATE ON orders TO n8n_workflow;
619
+ GRANT SELECT ON users TO n8n_workflow;
620
+
621
+ -- ❌ Bad - too much access
622
+ GRANT ALL PRIVILEGES TO n8n_workflow;
623
+ ```
624
+
625
+ ### 3. Validate Input Data
626
+ ```javascript
627
+ // Code node - validate before write
628
+ const email = $json.email;
629
+ const name = $json.name;
630
+
631
+ // Validation
632
+ if (!email || !email.includes('@')) {
633
+ throw new Error('Invalid email address');
634
+ }
635
+
636
+ if (!name || name.length < 2) {
637
+ throw new Error('Invalid name');
638
+ }
639
+
640
+ // Sanitization
641
+ return [{
642
+ json: {
643
+ email: email.toLowerCase().trim(),
644
+ name: name.trim()
645
+ }
646
+ }];
647
+ ```
648
+
649
+ ### 4. Encrypt Sensitive Data
650
+ ```javascript
651
+ // Code node - encrypt before storage
652
+ const crypto = require('crypto');
653
+
654
+ const algorithm = 'aes-256-cbc';
655
+ const key = Buffer.from($credentials.encryptionKey, 'hex');
656
+ const iv = crypto.randomBytes(16);
657
+
658
+ const cipher = crypto.createCipheriv(algorithm, key, iv);
659
+ let encrypted = cipher.update($json.sensitive_data, 'utf8', 'hex');
660
+ encrypted += cipher.final('hex');
661
+
662
+ return [{
663
+ json: {
664
+ encrypted_data: encrypted,
665
+ iv: iv.toString('hex')
666
+ }
667
+ }];
668
+ ```
669
+
670
+ ---
671
+
672
+ ## Common Gotchas
673
+
674
+ ### 1. ❌ Wrong: Unbounded queries
675
+ ```sql
676
+ SELECT * FROM large_table -- Could return millions
677
+ ```
678
+
679
+ ### ✅ Correct: Use LIMIT
680
+ ```sql
681
+ SELECT * FROM large_table
682
+ ORDER BY created_at DESC
683
+ LIMIT 1000
684
+ ```
685
+
686
+ ### 2. ❌ Wrong: String concatenation in queries
687
+ ```javascript
688
+ query: "SELECT * FROM users WHERE id = '{{$json.id}}'"
689
+ ```
690
+
691
+ ### ✅ Correct: Parameterized queries
692
+ ```javascript
693
+ query: "SELECT * FROM users WHERE id = $1",
694
+ parameters: ["={{$json.id}}"]
695
+ ```
696
+
697
+ ### 3. ❌ Wrong: No transaction for multi-step operations
698
+ ```
699
+ INSERT into orders
700
+ INSERT into order_items // Fails → orphaned order record
701
+ ```
702
+
703
+ ### ✅ Correct: Use transaction
704
+ ```
705
+ BEGIN
706
+ INSERT into orders
707
+ INSERT into order_items
708
+ COMMIT (or ROLLBACK on error)
709
+ ```
710
+
711
+ ### 4. ❌ Wrong: Processing all items at once
712
+ ```
713
+ SELECT 1000000 records → Process all → OOM error
714
+ ```
715
+
716
+ ### ✅ Correct: Batch processing
717
+ ```
718
+ SELECT records → Split In Batches (1000) → Process → Loop
719
+ ```
720
+
721
+ ---
722
+
723
+ ## Real Template Examples
724
+
725
+ From n8n template library (456 database templates):
726
+
727
+ **Data Sync**:
728
+ ```
729
+ Schedule → Postgres (SELECT new records) → Transform → MySQL (INSERT)
730
+ ```
731
+
732
+ **ETL Pipeline**:
733
+ ```
734
+ Schedule → [Multiple DB reads] → Merge → Transform → Warehouse (INSERT)
735
+ ```
736
+
737
+ **Backup**:
738
+ ```
739
+ Schedule → Postgres (SELECT all) → JSON → Google Drive (upload)
740
+ ```
741
+
742
+ Browse the n8n template library (n8n.io/workflows) for more examples!
743
+
744
+ ---
745
+
746
+ ## Checklist for Database Workflows
747
+
748
+ ### Planning
749
+ - [ ] Identify source and target databases
750
+ - [ ] Understand schema differences
751
+ - [ ] Plan transformation logic
752
+ - [ ] Consider batch size for large datasets
753
+ - [ ] Design error handling strategy
754
+
755
+ ### Implementation
756
+ - [ ] Use parameterized queries (never concatenate)
757
+ - [ ] Add LIMIT to all SELECT queries
758
+ - [ ] Use appropriate operation (INSERT/UPDATE/UPSERT)
759
+ - [ ] Configure credentials properly
760
+ - [ ] Test with small dataset first
761
+
762
+ ### Performance
763
+ - [ ] Add database indexes for queries
764
+ - [ ] Use batch operations
765
+ - [ ] Implement pagination for large datasets
766
+ - [ ] Configure connection pooling
767
+ - [ ] Monitor query execution times
768
+
769
+ ### Security
770
+ - [ ] Use parameterized queries (SQL injection prevention)
771
+ - [ ] Least privilege database user
772
+ - [ ] Validate and sanitize input
773
+ - [ ] Encrypt sensitive data
774
+ - [ ] Never log sensitive data
775
+
776
+ ### Reliability
777
+ - [ ] Add transaction handling if needed
778
+ - [ ] Check rows affected
779
+ - [ ] Handle constraint violations
780
+ - [ ] Implement retry logic
781
+ - [ ] Add Error Trigger workflow
782
+
783
+ ---
784
+
785
+ ## Summary
786
+
787
+ **Key Points**:
788
+ 1. **Always use parameterized queries** (prevent SQL injection)
789
+ 2. **Batch processing** for large datasets
790
+ 3. **Transaction handling** for multi-step operations
791
+ 4. **Limit result sets** to avoid memory issues
792
+ 5. **Validate input data** before writes
793
+
794
+ **Pattern**: Trigger → Query → Transform → Write → Verify
795
+
796
+ **Related**:
797
+ - [http_api_integration.md](http_api_integration.md) - Fetching data to store in DB
798
+ - [scheduled_tasks.md](scheduled_tasks.md) - Periodic database maintenance