<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Juan Cantón Rodríguez</title>
    <description>The latest articles on DEV Community by Juan Cantón Rodríguez (@srcanton).</description>
    <link>https://dev.to/srcanton</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4018882%2F95e3fdf9-a4f3-4923-9c54-45039a38b44c.png</url>
      <title>DEV Community: Juan Cantón Rodríguez</title>
      <link>https://dev.to/srcanton</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/srcanton"/>
    <language>en</language>
    <item>
      <title>The ISO 27001 controls that actually matter when you connect two systems</title>
      <dc:creator>Juan Cantón Rodríguez</dc:creator>
      <pubDate>Sun, 12 Jul 2026 08:14:37 +0000</pubDate>
      <link>https://dev.to/srcanton/the-iso-27001-controls-that-actually-matter-when-you-connect-two-systems-4hon</link>
      <guid>https://dev.to/srcanton/the-iso-27001-controls-that-actually-matter-when-you-connect-two-systems-4hon</guid>
      <description>&lt;p&gt;I build integrations for a living: ecommerce to ERP, CRM to invoicing, and lately ERPs to AI agents. Every one of them has the same quiet security problem, and most "ISO 27001 for developers" content misses it because it stays at the policy level.&lt;/p&gt;

&lt;p&gt;Here is the thing nobody says out loud: &lt;strong&gt;integrating two systems means taking data out of the place where it lived protected and moving it across a network you do not control.&lt;/strong&gt; Names, tax IDs, amounts, addresses, emails. That journey is the attack surface. Not the database. The trip.&lt;/p&gt;

&lt;p&gt;You do not need to be ISO 27001 certified to care about this. Certification is a formal audit that makes sense when a client demands it. Applying the controls is what actually protects the data, and you can do it on the next integration you ship. So let me map the ISO 27001:2022 Annex A controls that land directly on integration work, in plain terms, with what I actually do for each one.&lt;/p&gt;

&lt;h2&gt;
  
  
  The controls, translated to integration reality
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A.8.24 Use of cryptography.&lt;/strong&gt; Data in transit goes over a network you do not own. In the clear, anyone on the path reads it. Rule: TLS 1.2+ end to end on every call, no plain HTTP between services, ever. Any intermediate copy (queue, staging, cache) is encrypted at rest.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A.5.17 Authentication information.&lt;/strong&gt; The API key is the key to the house. Whoever holds it, gets in. So: keys in a secret manager, never in code or in the browser bundle, rotated on a schedule and on any suspicion of a leak. One credential per integration so you can revoke it in isolation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A.5.15 / A.5.16 Access control and identity management.&lt;/strong&gt; The temptation is to reuse the admin key because "it works with everything". If it leaks, the whole ERP leaks with it. Real least privilege instead: each flow gets a credential scoped to exactly what it needs. Read-only if it only reads. Scope is a decision, not a default.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A.5.14 Information transfer.&lt;/strong&gt; A lazy integration drags a full record from one system to another "just in case". Every extra field is one more thing that can leak. Move only the fields the destination genuinely needs, and write down the data flow (what leaves, where to, on what legal basis).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A.8.15 / A.8.16 Logging and monitoring.&lt;/strong&gt; Without traces, an integration is a black box. You cannot tell if an invoice was duplicated, if a webhook failed silently, or if someone is probing your credentials. Every sync leaves a queryable, tamper-evident record, and anomalies fire alerts: spikes in auth failures, stuck queues, retries that never resolve.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A.5.19 / A.5.21 Suppliers and ICT supply chain.&lt;/strong&gt; This one flips the mirror. When I connect your systems, I become a supplier that touches your data. And behind me sits the hosting, the iPaaS, the payment gateway. All of them are in scope. That means a written data processing agreement and a real look at the sub-processors and where the data is hosted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A.8.12 / A.5.34 Data leakage prevention and PII protection.&lt;/strong&gt; The place personal data leaks most often by accident is not the database. It is the logs. A full payload dumped into a log line exposes tax IDs, emails and addresses to anyone with log access. Log just enough to debug, with sensitive fields masked.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A.8.10 / A.8.11 Information deletion and data masking.&lt;/strong&gt; Integrations leave copies behind: queues, test environments, backups. Real customer data living in places nobody looks at again. Define retention per flow, delete for real when due, and work with masked or synthetic data outside production.&lt;/p&gt;

&lt;h2&gt;
  
  
  The two questions I always get
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;"Is n8n / Zapier / Make secure under ISO 27001?"&lt;/strong&gt; The tool alone does not give you control. The control is in how you use it, and the biggest variable is where the data lives. A cloud iPaaS puts that third party into your supply chain and routes your data through their infra. Self-hosted n8n keeps the data on your own server, which massively simplifies scope. (I maintain an open-source &lt;a href="https://francodesystems.com/en/open-source/n8n-nodes-holded" rel="noopener noreferrer"&gt;n8n node for Holded&lt;/a&gt;, MIT, precisely so people can automate without shipping their data to one more vendor.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"What about AI agents and MCP?"&lt;/strong&gt; New actor, same controls. When you connect an agent (say via MCP, the protocol that lets an assistant read and act on external systems with typed permissions), you are giving a model access to real data. Scoped read-only credentials when reading is enough, traceability of every action, minimisation of what the model sees, and control over which provider that data reaches. The framework does not change just because the caller is a model.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest footer
&lt;/h2&gt;

&lt;p&gt;I am not going to tell you I am ISO 27001 certified, because I am not, and saying so without an accredited body's badge would be a lie. What I will tell you is that these controls are cheap to apply if you build them in from the start and expensive to retrofit after a leak. The certification is the paperwork. The controls are the actual security.&lt;/p&gt;

&lt;p&gt;Full version, with the framework reference table (93 controls, 4 themes, the 2013 to 2022 transition that closed in October 2025): &lt;strong&gt;&lt;a href="https://francodesystems.com/en/blog/iso-27001-integrations" rel="noopener noreferrer"&gt;francodesystems.com/en/blog/iso-27001-integrations&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you handle other people's data through integrations, which controls have bitten you in production? Curious to compare notes in the comments.&lt;/p&gt;

</description>
      <category>security</category>
      <category>api</category>
      <category>automation</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I didn't write my n8n node's 323 operations. I scraped the docs and generated them.</title>
      <dc:creator>Juan Cantón Rodríguez</dc:creator>
      <pubDate>Tue, 07 Jul 2026 08:24:16 +0000</pubDate>
      <link>https://dev.to/srcanton/i-didnt-write-my-n8n-nodes-323-operations-i-scraped-the-docs-and-generated-them-1pjn</link>
      <guid>https://dev.to/srcanton/i-didnt-write-my-n8n-nodes-323-operations-i-scraped-the-docs-and-generated-them-1pjn</guid>
      <description>&lt;p&gt;Last month my n8n community node for &lt;a href="https://francodesystems.com/open-source/n8n-nodes-holded" rel="noopener noreferrer"&gt;Holded&lt;/a&gt; — the ERP used by 100,000+ Spanish SMEs — got the &lt;strong&gt;verified&lt;/strong&gt; badge and became &lt;a href="https://n8n.io/integrations/holded/" rel="noopener noreferrer"&gt;the only Holded integration on n8n.io&lt;/a&gt;.&lt;br&gt;
A&lt;br&gt;
It covers &lt;strong&gt;42 resources and 323 operations&lt;/strong&gt; of the Holded API v2, plus a trigger node for 58 webhook events.&lt;/p&gt;

&lt;p&gt;Here's the part that matters: I hand-wrote almost none of it. This is the story of shipping a huge API surface as a solo dev by treating the node as a &lt;em&gt;compiler target&lt;/em&gt; instead of a codebase.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why bother with a node for a Spanish ERP?
&lt;/h2&gt;

&lt;p&gt;Spanish SMEs are migrating to n8n from Make and Zapier for a very unglamorous reason: &lt;strong&gt;per-operation pricing&lt;/strong&gt;. The moment you automate invoicing properly — say, an ecommerce store pushing 1,000+ orders a month into accounting — a per-task bill stops making sense. Self-hosting also plays well with GDPR anxiety.&lt;/p&gt;

&lt;p&gt;The blocker was that n8n had zero Holded connectivity. Everyone was gluing HTTP Request nodes to a quirky API. That's the gap.&lt;/p&gt;
&lt;h2&gt;
  
  
  The math that forbids hand-writing
&lt;/h2&gt;

&lt;p&gt;Holded's API v2 is big: invoices, contacts, products, stock, CRM funnels, projects, payroll, remittances… When I mapped it, I got &lt;strong&gt;591 endpoints across 42 resources&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;An n8n node describes every operation declaratively: resource options, operation options, every parameter with its type, defaults, and &lt;code&gt;displayOptions&lt;/code&gt; controlling when it appears in the UI. Written by hand, that's maybe 60–80 lines per operation. Times 323.&lt;/p&gt;

&lt;p&gt;That's a ~25,000-line file I would have to &lt;em&gt;maintain by hand&lt;/em&gt; every time Holded touches their API. No.&lt;/p&gt;
&lt;h2&gt;
  
  
  The pipeline: scrape → spec → generate
&lt;/h2&gt;

&lt;p&gt;So the repo has a &lt;code&gt;spec/&lt;/code&gt; folder that most n8n nodes don't:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;spec/
├── scrape-v2.py        # scrapes the official API docs
├── v2-endpoints.json   # 72,000 lines: every endpoint, param, type
└── gen-v2.py           # 888 lines that write the node for me
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;scrape-v2.py&lt;/code&gt;&lt;/strong&gt; walks Holded's API reference and dumps every endpoint — path, method, params, body schema — into &lt;code&gt;v2-endpoints.json&lt;/code&gt; (~72k lines of structured truth).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;gen-v2.py&lt;/code&gt;&lt;/strong&gt; compiles that spec into &lt;code&gt;V2Generated.ts&lt;/code&gt;: &lt;strong&gt;22,000 lines&lt;/strong&gt; of n8n property descriptors — every resource, operation, and field, with correct &lt;code&gt;displayOptions&lt;/code&gt; wiring.&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;173-line dispatcher&lt;/strong&gt; routes &lt;code&gt;(resource, operation)&lt;/code&gt; pairs to generic HTTP execution. The actual &lt;code&gt;.node.ts&lt;/code&gt; shell is 355 lines.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The generated file opens with the only comment that matters:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// AUTO-GENERATED by spec/gen-v2.py from spec/v2-endpoints.json.&lt;/span&gt;
&lt;span class="c1"&gt;// Do not edit by hand — regenerate with `python3 spec/gen-v2.py`.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When Holded changes their API, I re-scrape, re-generate, diff, release. Maintenance went from "career" to "afternoon".&lt;/p&gt;

&lt;h2&gt;
  
  
  What codegen can't do (and what I still wrote by hand)
&lt;/h2&gt;

&lt;p&gt;Generation gets you &lt;em&gt;coverage&lt;/em&gt;. It does not get you &lt;em&gt;good UX&lt;/em&gt;. Three things stayed human:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Contact resource is fully handwritten.&lt;/strong&gt; It's the resource everyone touches first, and the generated version was technically correct but clumsy — search deserves first-class &lt;code&gt;email&lt;/code&gt; / &lt;code&gt;vatNumber&lt;/code&gt; / &lt;code&gt;phone&lt;/code&gt; fields, not a generic filter collection. High-traffic paths earn hand-polish; the long tail stays generated.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Naming and grouping.&lt;/strong&gt; A dropdown with 42 resources is only usable if names match what users see in Holded's UI, not what the API paths say.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;n8n's lint rules.&lt;/strong&gt; &lt;code&gt;eslint-plugin-n8n-nodes-base&lt;/code&gt; has strong opinions (alphabetized options, description formats, casing). The generator had to learn them — fixing lint output &lt;em&gt;in the generator&lt;/em&gt; instead of in the generated file is the whole trick of this approach.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting the "verified" badge
&lt;/h2&gt;

&lt;p&gt;n8n's verification reviews your code before the node becomes installable on n8n Cloud (community nodes otherwise need self-hosting). What it costs you: strict linting, no runtime dependencies, real docs, and review iterations measured in days-to-weeks.&lt;/p&gt;

&lt;p&gt;What you get: distribution. Verified nodes show up in the in-app node panel for every n8n Cloud user, plus &lt;a href="https://n8n.io/integrations/holded/" rel="noopener noreferrer"&gt;a public integration page&lt;/a&gt;. For a niche B2B tool, that's the difference between "GitHub repo with 12 stars" and "discoverable by every n8n user who types your product's name".&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaways if you're building a big-API node
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;If the API has &amp;gt;50 operations, don't type them. Generate them.&lt;/strong&gt; A one-day scraper plus a one-week generator beats months of typing and years of drift.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Spec first.&lt;/strong&gt; The intermediate JSON is the asset — the TS is disposable output. I can regenerate for a future n8n API version, or even for a different platform, from the same spec.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hand-write the top 10% by traffic.&lt;/strong&gt; Generated long tail, polished hot paths.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Teach the generator the linter's opinions.&lt;/strong&gt; Never fix generated files by hand, not even once.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Webhooks are half the value.&lt;/strong&gt; The trigger node (18 objects / 58 events: &lt;code&gt;invoice.create&lt;/code&gt;, &lt;code&gt;stock.update&lt;/code&gt;, …) is what turns an ERP from "destination" into "event source".&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;n8n&lt;/strong&gt;: Settings → Community Nodes → &lt;code&gt;@francodesystems-npm/n8n-nodes-holded&lt;/code&gt; (or find "Holded" directly on n8n Cloud)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repo&lt;/strong&gt;: &lt;a href="https://github.com/francodesystems/n8n-nodes-holded" rel="noopener noreferrer"&gt;github.com/francodesystems/n8n-nodes-holded&lt;/a&gt; (MIT)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Docs, coverage matrix and use cases&lt;/strong&gt;: &lt;a href="https://francodesystems.com/open-source/n8n-nodes-holded" rel="noopener noreferrer"&gt;francodesystems.com/open-source/n8n-nodes-holded&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;If you're migrating between Holded API v1 and v2, I wrote up &lt;a href="https://francodesystems.com/blog/holded-api-v1-v2-trampas-migrar" rel="noopener noreferrer"&gt;the 8 traps that will actually bite you&lt;/a&gt; (Spanish).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ready-to-import workflow templates (Shopify → invoices, Stripe → paid invoices, overdue-invoice chasing…) are landing on my n8n creator profile — I'll update this post with the links as they're approved.&lt;/p&gt;

&lt;p&gt;Questions about the codegen pipeline or the verification process — ask away in the comments. 🇪🇸&lt;/p&gt;

</description>
      <category>n8n</category>
      <category>opensource</category>
      <category>automation</category>
      <category>typescript</category>
    </item>
  </channel>
</rss>
