N8n Code To Native logo

N8n Code To Native

Community
MorrisLu-Taipei
n8n-code-to-native

Convert n8n Code (JS) nodes into native declarative nodes (Set / Filter / Merge / Crypto / Aggregate / ConvertToFile) so the workflow is readable by n8n-experts who don't know JavaScript. Use when the user asks to "原生化", "改成 n8n 原生 node", "reduce Code nodes", or refactor an n8n workflow JSON to remove JS where possible.

Overview

PublisherMorrisLu-Taipei
RepositoryTigerAI-A2A-Code2n8n-Skill-Pack
Skill namen8n-code-to-native
Stars
86
Forks
26
Bundled files
Instructions only
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by MorrisLu-Taipei on GitHub. Read the source before you install it.

Installation

Install the N8n Code To Native AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/MorrisLu-Taipei/TigerAI-A2A-Code2n8n-Skill-Pack/tree/main/skills/tigerai/n8n-code-to-native
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/MorrisLu-Taipei/TigerAI-A2A-Code2n8n-Skill-Pack.git /tmp/TigerAI-A2A-Code2n8n-Skill-Pack
mkdir -p .claude/skills
cp -r /tmp/TigerAI-A2A-Code2n8n-Skill-Pack/skills/tigerai/n8n-code-to-native .claude/skills/n8n-code-to-native
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable N8n Code To Native in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads N8n Code To Native on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so N8n Code To Native is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

n8n Code → Native node refactor

The audience for these workflows is n8n experts who read the canvas but cannot read JavaScript. A Code node is opaque to them. Replacing it with declarative native nodes — even if the result has more nodes — is the right trade-off.

Hard rules

  1. Every conversion bumps the workflow's sticky note with a v<N> <date>: ... change-history line. The first sticky note in the file is the change log. Do not skip this — it is the audit trail.
  2. Never break in-place semantics without confirming with the user. If a single Code node becomes 1 native node → safe (swap-in-place, no connection edits). If it becomes 2+ nodes → confirm scope first (connections must be re-wired).
  3. Connections in n8n JSON are by node name. Keep the node name identical when swapping types in-place; connections will survive automatically.
  4. n8n expressions support array methods. .map, .reduce, .filter, .includes, regex .match, Math.max(...arr), ternaries, optional chaining ?., nullish ??, spread ... — all work inside ={{ ... }}. Use them. They are NOT "code" — they are expressions inside declarative nodes.
  5. Do NOT introduce IIFE (() => { ... })() in expressions. That is JS in disguise and defeats the purpose. If the logic needs IIFE, either (a) split into two sequential Set nodes so each step is one expression, or (b) keep it as a Code node.
  6. Keep what genuinely belongs in code. See "Keep as Code" below. Forcing every Code node into native makes things worse.

Process

Step 1 — Inventory

For each workflow JSON, list every n8n-nodes-base.code node with its name and JS body. Group by intent (callback builder, parser, aggregator, file builder, …). Useful Python:

python
import json, glob
for f in sorted(glob.glob("*.json")):
    d = json.load(open(f, encoding="utf-8"))
    codes = [n for n in d["nodes"] if n["type"] == "n8n-nodes-base.code"]
    for n in codes:
        print(f, "::", n["name"], "::", n["parameters"]["jsCode"][:120])

Step 2 — Classify each Code node

PatternAction
Returns one item with field assignments (constants + expressions)Set (swap-in-place)
Reduces array → scalar (sum/count/max/avg) for fieldsSet with .reduce/.map/Math.max expression
Filters items by criteriaFilter node (in-place)
Pairs items from two upstream nodes by indexMerge (mode: combine, combineBy: position) + Set (structural, 2 nodes)
Writes file from stringSet (build content) + Convert to File (structural, 2 nodes)
Parses LLM JSON with multi-shape fallback (try/catch over Ollama/OpenAI shapes, regex extract on parse failure)Keep as Code — fallback logic is too branchy for expressions
Pure-JS crypto polyfill (SHA-1, UUID v5)Crypto node (n8n native) for SHA-1; UUID v5 needs Crypto + Set + bit-twiddling expressions — feasible but separate effort. Default: keep
Dynamic nested object/filter assembly with conditional branches (e.g. Qdrant filter built from for-loops)Keep as Code

Step 3 — Convert in-place (Set node, typeVersion 3.4)

The Set v3.4 node shape used in these workflows:

json
{
  "name": "Build Callback",
  "type": "n8n-nodes-base.set",
  "typeVersion": 3.4,
  "parameters": {
    "assignments": {
      "assignments": [
        { "id": "a1", "name": "folder_path",  "value": "={{ $('Extract Payload').first().json.folder_path }}", "type": "string" },
        { "id": "a2", "name": "chunk_count",  "value": "={{ $input.all().reduce((s, x) => s + (x.json.chunk_count || 0), 0) }}", "type": "number" },
        { "id": "a3", "name": "results",      "value": "={{ $input.all().map(i => i.json).filter(r => r.file_name) }}", "type": "array" },
        { "id": "a4", "name": "status",       "value": "Step 03 進行中", "type": "string" }
      ]
    },
    "options": {}
  }
}
  • value starting with = is treated as an expression. Otherwise it is a literal.
  • type must match the expression's actual return: string, number, boolean, array, object.
  • For empty literal arrays/objects, use ={{ [] }} / ={{ {} }} (expression returning the value) with type: "array" / "object".
  • Newlines inside expression-built strings: use \\n in the JSON source (which encodes to \n in the actual string value).
  • Preserve every other field on the node (id, position, credentials, disabled, notes, webhookId, …) — only change type, typeVersion, and parameters. Conversions are surgical.

Step 4 — Update the sticky note (MANDATORY)

The first sticky note in the workflow is the change log. Append a line like:

--- v1.0.2 2026-05-30: Code -> Set (Build Callback x5 + Build Exclude Request + Build RAG Context, native expressions with .reduce/.map/.filter)

Format: v<file-internal-version> <YYYY-MM-DD>: <what changed in plain language>. Do not rename the workflow file (filename version stays — internal patch bump only). If the file has no sticky note, create one — that file was non-conformant.

Step 5 — Verify before committing

  • Re-read the node JSON: type is n8n-nodes-base.set, typeVersion is 3.4, parameters has assignments.assignments[].
  • Node count and connections unchanged for in-place swaps (count by n['type'] before/after; connections map is by name → still valid).
  • Manually import one converted workflow into n8n and run an execution if the user can spare the time; otherwise list the converted nodes in the commit message so the user can spot-check.

What to KEEP as Code (do not convert)

  1. LLM output parsers with multi-shape fallback. Example pattern:

    js
    const raw = r.message?.content || r.choices?.[0]?.message?.content || r.response || '';
    let s = raw.trim().replace(/^```(?:json)?\s*/i, '').replace(/```\s*$/i, '').trim();
    let parsed; try { parsed = JSON.parse(s); } catch (e) { const m = s.match(/\[[\s\S]*\]/); ... }

    The branchy try/catch + regex-extract fallback is unreadable as a chain of native nodes.

  2. Pure-JS crypto polyfills. n8n sandbox blocks require('crypto'). SHA-1 and UUID v5 implementations are long but they belong in Code (or can be replaced by the native Crypto node — separate, deeper refactor).

  3. Dynamic nested object assembly. Building something like a Qdrant filter: { should: [...] } where the should array is built from a for-loop over field names × keywords is genuinely loop-shaped. Native node doesn't model this naturally.

  4. Per-item branching on shape detection. e.g. if Array.isArray(item.json) ... else if Array.isArray(item.json.data) ... else if (item.json.text) ... else continue. An IF node only branches the whole stream, not per-item.

n8n expression cheat sheet

  • Current item: {{ $json.x }} (NOT $.x)
  • All items from current input: {{ $input.all() }} → returns [{json,binary}, ...]
  • First item: {{ $input.first().json.x }}
  • Reference another node by name: {{ $('Node Name').first().json.x }} or {{ $('Node Name').all() }}
  • Array methods: {{ $input.all().map(i => i.json.x) }}, {{ ... .filter(x => x > 0) }}, {{ ... .reduce((s, x) => s + x.json.n, 0) }}
  • Conditionals: {{ x > 0 ? 'pos' : 'neg' }}, {{ x?.y ?? 0 }}
  • Math: {{ Math.max(...arr) }}, {{ Math.round(n) }}
  • String building: {{ 'Got ' + n + ' items' }} or backticks via {{ \Got ${n} items` }}` (works in expressions)

Common pitfalls

  • includeOtherFields semantics: by default Set v3.4 returns ONLY the assigned fields. To preserve input fields and add new ones, set parameters.includeOtherFields: true at the parameter level (NOT inside options). Verify in the n8n UI after import.
  • $('NodeName').first() errors if the referenced node hasn't run on this execution path. Wrap with ?.: {{ $('X')?.first()?.json?.y ?? 0 }}. If the original Code wrapped this in try/catch, that's a signal the node may be optional — preserve safety.
  • Sticky note encoding: workflow JSONs are UTF-8. When editing via Python, open with encoding='utf-8' and dump with ensure_ascii=False to keep Chinese readable in the JSON.
  • Don't rename nodes. Connections key by node name. Renaming requires also updating d['connections']. Not worth it.
  • Math.max(...[]) returns -Infinity. Guard with (arr.length ? Math.max(...arr) : 0).

When user asks "原生化" — recommended interaction

  1. Inventory all Code nodes; classify by the table above.
  2. Report counts: how many fall in each category (in-place Set / structural / keep).
  3. Recommend doing in-place swaps first as Phase 1 (safe, no connection edits, fast).
  4. For structural (Phase 2) and complex (Phase 3), get explicit go-ahead per node — they involve adding nodes or non-trivial expression chains.
  5. After each phase: commit with a clear message listing what converted, what stayed and why; push.

Frequently asked questions

What does the N8n Code To Native AI skill do?

Convert n8n Code (JS) nodes into native declarative nodes (Set / Filter / Merge / Crypto / Aggregate / ConvertToFile) so the workflow is readable by n8n-experts who don't know JavaScript. Use when the user asks to "原生化", "改成 n8n 原生 node", "reduce Code nodes", or refactor an n8n workflow JSON to remove JS where possible.

Why use N8n Code To Native on TypingMind?

Because you install it once and use it with any model. N8n Code To Native is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install N8n Code To Native in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/MorrisLu-Taipei/TigerAI-A2A-Code2n8n-Skill-Pack/tree/main/skills/tigerai/n8n-code-to-native. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use N8n Code To Native?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with N8n Code To Native?

As many as you like. As long as a model supports skills, you can use N8n Code To Native with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the N8n Code To Native AI skill free?

Yes. It is published on GitHub by MorrisLu-Taipei under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇