{ "name": "78 Schema check (reference)", "nodes": [ { "name": "Check webhook", "type": "n8n-nodes-base.webhook", "typeVersion": 2, "parameters": { "httpMethod": "POST", "path": "my-check", "responseMode": "responseNode", "options": {} }, "position": [ 0, 300 ], "id": "c042a07f-a8f7-565f-b562-64bdc1e951e1", "webhookId": "a46fe9fe-4604-5c34-8435-2d87f4063861" }, { "name": "Get schema", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.2, "parameters": { "url": "=http://data-api:8800/schemas/{{ encodeURIComponent($json.body.schema ?? '') }}", "options": { "response": { "response": { "neverError": true } } } }, "position": [ 220, 300 ], "id": "e7a886c9-f582-578b-bb91-75b1b0464de0" }, { "name": "Known schema?", "type": "n8n-nodes-base.if", "typeVersion": 2.2, "parameters": { "conditions": { "options": { "caseSensitive": true, "leftValue": "", "typeValidation": "loose", "version": 2 }, "conditions": [ { "id": "c1", "leftValue": "={{ $json.fields !== undefined }}", "rightValue": "", "operator": { "type": "boolean", "operation": "true", "singleValue": true } } ], "combinator": "and" }, "options": {} }, "position": [ 440, 300 ], "id": "368be2eb-9471-523a-88c7-adb0d427375b" }, { "name": "Unknown schema", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1.1, "parameters": { "respondWith": "json", "responseBody": "={{ JSON.stringify({ error: 'unknown schema', schema: $('Check webhook').first().json.body.schema ?? null }) }}", "options": { "responseCode": 400 } }, "position": [ 660, 560 ], "id": "482f3c8d-6a2c-5b9a-9f5c-056b8bf51986" }, { "name": "Check rows", "type": "n8n-nodes-base.code", "typeVersion": 2, "parameters": { "jsCode": "const body = $('Check webhook').first().json.body;\nconst schema = $json;\nlet items = body.items;\nif (typeof body.csv === 'string') {\n const [head, ...lines] = body.csv.trim().split(/\\r?\\n/);\n const cols = head.split(',').map((c) => c.trim());\n items = lines.map((line) => Object.fromEntries(line.split(',').map((v, i) => [cols[i], v.trim()])));\n}\nconst isDate = (v) => { if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(v)) return false; const d = new Date(v + 'T00:00:00Z'); return !isNaN(d) && d.toISOString().slice(0, 10) === v; };\nconst seen = new Set();\nconst rows = (Array.isArray(items) ? items : []).map((r, index) => {\n const errors = [];\n for (const [name, f] of Object.entries(schema.fields)) {\n const v = r?.[name];\n const empty = v === undefined || v === null || (typeof v === 'string' && v.trim() === '') || (Array.isArray(v) && v.length === 0);\n if (empty) { if (f.required) errors.push(`missing:${name}`); continue; }\n const s = String(v);\n if (f.type === 'string') { if (f.max && s.length > f.max) errors.push(`too_long:${name}`); if (f.min && s.length < f.min) errors.push(`too_short:${name}`); }\n if (f.type === 'enum' && !f.values.includes(s)) errors.push(`not_allowed:${name}`);\n if (f.type === 'email' && !/^[\\w.+-]+@[\\w-]+(\\.[\\w-]+)+$/.test(s)) errors.push(`bad_format:${name}`);\n if (f.type === 'phone' && !/^0\\d{1,2}-\\d{3,4}-\\d{4}$/.test(s)) errors.push(`bad_format:${name}`);\n if (f.type === 'date' && !isDate(s)) errors.push(`bad_format:${name}`);\n if (f.type === 'pattern' && !new RegExp(f.pattern).test(s)) errors.push(`bad_format:${name}`);\n if (f.type === 'list') { if (!Array.isArray(v)) errors.push(`bad_format:${name}`); else { if (f.min_items && v.length < f.min_items) errors.push(`too_few:${name}`); if (f.max_items && v.length > f.max_items) errors.push(`too_many:${name}`); } }\n }\n const key = String(r?.[schema.key] ?? '');\n if (key && seen.has(key)) errors.push('duplicate_in_batch');\n if (key) seen.add(key);\n return { row: index + 1, key, ok: errors.length === 0, errors };\n});\nconst codes = {};\nfor (const row of rows) for (const e of row.errors) codes[e] = (codes[e] ?? 0) + 1;\nconst valid = rows.filter((r) => r.ok).length;\nreturn [{ json: { schema: schema.name, batch: body.batch ?? '', total: rows.length, valid, invalid: rows.length - valid, rows, codes } }];" }, "position": [ 660, 300 ], "id": "d5d18640-aebb-5227-905c-92435b892892" }, { "name": "Send result", "type": "n8n-nodes-base.respondToWebhook", "typeVersion": 1.1, "parameters": { "respondWith": "json", "responseBody": "={{ $json }}", "options": {} }, "position": [ 880, 300 ], "id": "3cf4a033-8e67-5be5-8392-473991da1f4d" } ], "connections": { "Check webhook": { "main": [ [ { "node": "Get schema", "type": "main", "index": 0 } ] ] }, "Get schema": { "main": [ [ { "node": "Known schema?", "type": "main", "index": 0 } ] ] }, "Known schema?": { "main": [ [ { "node": "Check rows", "type": "main", "index": 0 } ], [ { "node": "Unknown schema", "type": "main", "index": 0 } ] ] }, "Check rows": { "main": [ [ { "node": "Send result", "type": "main", "index": 0 } ] ] } }, "settings": { "executionOrder": "v1" } }