An n8n node is data, not code
The official template ships 50 files and needs 600 MB of dependencies to build. We emit six files and 2.5 kB. Here is why the difference is not a trick.
Building an n8n community node is supposed to require a toolchain. You install a template, pull down a few hundred megabytes of dependencies, write TypeScript, compile it, and publish the result. That is the documented path, and it works.
It is also almost entirely unnecessary, and the reason is worth understanding even if you never use our builder.
The whole node lives in a JSON description
A declarative n8n node has no logic of its own. Everything it does — which fields appear, what the user can fill in, where each value lands in the outgoing HTTP request — is described in data. n8n reads that description and does the work.
Which means the JavaScript in a finished node is not the node. It is a four line adapter that hands the description to n8n:
const definition = require('./definition.json');
class Stripe {
constructor() {
this.description = structuredClone(definition);
}
}
exports.Stripe = Stripe;That file is byte identical in every node anyone has ever built, apart from the class name. It is not where the behaviour is. It is a handle.
If the only code is identical in every case, it is not code. It is a formality that a compiler was being used to produce.
So what is the toolchain for?
Mostly for turning TypeScript into that JSON. The template gives you types, a build step and a bundler, and at the end it produces a description and a shim. If you can produce the description another way, none of the rest is load-bearing.
We produce it from a form. The difference in output is not subtle:
- n8n's TypeScript template: 68.6 kB across 50 files, needing roughly 600 MB of
node_modulesto build. - Ours: 2.5 kB across 6 files, with zero dependencies.
Same installable package. Same behaviour inside n8n. The 600 MB was scaffolding for a building that turns out to be one room.
The idea that actually matters
If you take one thing from this, take this one, because it is the thing most people miss and it is what makes the whole approach work.
A field is not a form input. A field is a binding between a form input and one specific part of an HTTP request. Whether a value becomes a path segment, a query parameter, a body property or a header is the entire substance of what a node does.
Strip the bindings out and you are left with a settings panel that collects values and sends them nowhere.
What we did not do
We did not fork n8n, wrap it, or copy any of it. There is no n8n code in our dependencies and none in what we emit — the shim above imports one thing, and that thing is your own JSON file.
What we built is an independent implementation that targets the file format, the way any interoperable tool reads a format it did not design. n8n invented the format. We just noticed it did not need a compiler.