Ep 07: Ditch the External DB — Mastering Native Data Tables for CRUD
What Are Data Tables?
n8n 2.0's Data Tables is a built-in lightweight persistent store — think of it as a "workflow-dedicated simple database." No PostgreSQL, MongoDB, or Google Sheets setup required.
graph TB
subgraph "Traditional (Complex)"
W1[n8n Workflow] -->|"API call"| EXT[(External DB
PostgreSQL / Airtable)]
end
subgraph "Data Tables (Simple)"
W2[n8n Workflow] -->|"Native node"| DT[(Built-in Data Tables
Zero config, zero latency)]
end
style DT fill:#22c55e,stroke:#16a34a,color:#fff
style EXT fill:#ef4444,stroke:#dc2626,color:#fffUse Cases
| Scenario | Example | Traditional | Data Tables |
|---|---|---|---|
| Deduplication | Track processed email IDs | Redis / file locks | ✅ One table row |
| AI Prompt Mgmt | Store system prompts per scenario | Hardcoded / config files | ✅ Table-driven |
| Simple CRM | Track customer states | Airtable / Sheets | ✅ Native flow |
| Counters | Daily ticket count | PostgreSQL | ✅ Auto-increment |
CRUD Operations
Create
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Data Tables Node: Insert new record
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Operation: "Insert"
// Table: "processed_emails"
// Columns:
// email_id: {{ $json.messageId }} ← Dynamic from upstream
// processed_at: {{ $now.toISO() }} ← Built-in current time
// status: "completed" ← Static value
// Output includes auto-generated row ID:
// { "json": { "id": "row_abc123", "email_id": "msg_12345", ... } }
Read
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Data Tables Node: Query records
// ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
// Method 1: Get all rows (with optional Limit)
// Method 2: Filter by condition — email_id = {{ $json.currentEmailId }}
// Method 3: Get single row by ID — Row ID: {{ $json.rowId }}
Update & Delete
// Update: Operation "Update Row" — must specify Row ID
// - Row ID: {{ $json.id }}
// - status: "failed", retry_count: {{ $json.retry_count + 1 }}
// Delete: Operation "Delete Row" — Row ID: {{ $json.id }}
// ⚠️ Irreversible! Consider soft-delete (update status to "deleted") instead
Practical: Email Deduplication Workflow
graph TB
T[📧 Gmail Trigger] --> Extract[Set: Extract messageId]
Extract --> Query[Data Tables: Query
email_id = messageId]
Query --> Check{If: No results?}
Check -->|"✅ New email"| Process[Process Content]
Check -->|"❌ Already processed"| Skip[Skip]
Process --> Record[Data Tables: Insert
Record processed ID]
Record --> Notify[Slack: Notify]
style Check fill:#f59e0b,stroke:#d97706
style Query fill:#22c55e,stroke:#16a34a,color:#fffNext Episode
In Ep 08, we master n8n's workhorse for external communication — the HTTP Request node — covering authentication, pagination, and concurrency control.