<?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: Patrial</title>
    <description>The latest articles on DEV Community by Patrial (@danilopatrial).</description>
    <link>https://dev.to/danilopatrial</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%2F3989109%2Fc5515947-659d-4349-8036-27816f011168.png</url>
      <title>DEV Community: Patrial</title>
      <link>https://dev.to/danilopatrial</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/danilopatrial"/>
    <language>en</language>
    <item>
      <title>Sooket: build API middleware on a canvas - and run it inline in the request path</title>
      <dc:creator>Patrial</dc:creator>
      <pubDate>Wed, 17 Jun 2026 14:23:00 +0000</pubDate>
      <link>https://dev.to/danilopatrial/sooket-build-api-middleware-on-a-canvas-and-run-it-inline-in-the-request-path-1n01</link>
      <guid>https://dev.to/danilopatrial/sooket-build-api-middleware-on-a-canvas-and-run-it-inline-in-the-request-path-1n01</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; — Sooket is a visual editor whose output is a live HTTP endpoint. You drag nodes, connect them, and the graph executes &lt;em&gt;inside&lt;/em&gt; the request path — so unlike n8n/Make/Zapier (which run async, after the fact), it can actually shape the response a caller is waiting on. It's self-hosted, runs as a single Next.js process against a local SQLite file, and is source-available under FSL-1.1-MIT. Repo: &lt;a href="https://github.com/danilopatrial/sooket" rel="noopener noreferrer"&gt;github.com/danilopatrial/sooket&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The problem: every API grows a glue layer
&lt;/h2&gt;

&lt;p&gt;Sooner or later, every API needs a layer of logic in front of it. Throttle abusive callers. Strip PII before it hits a third party. Cache an expensive response. Validate a token. Fan a request out to an LLM.&lt;/p&gt;

&lt;p&gt;Today that glue is one of two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Hand-written middleware&lt;/strong&gt; you build, test, deploy, and babysit.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An automation tool&lt;/strong&gt; (n8n, Make, Zapier) — but those run &lt;em&gt;asynchronously&lt;/em&gt;, after the fact, and can't shape the response the caller is blocking on.
There's a gap between those two. Sooket is what goes in it.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What Sooket is
&lt;/h2&gt;

&lt;p&gt;Sooket is a visual canvas for the logic that sits &lt;em&gt;between&lt;/em&gt; a caller and your API. You drag nodes onto a canvas, connect them, and expose the whole pipeline as a single HTTP endpoint. Every request runs through the graph &lt;strong&gt;synchronously&lt;/strong&gt; and gets a response back.&lt;/p&gt;

&lt;p&gt;No separate service to host. No async queue. No cloud account. It runs as one Next.js process against a local SQLite file on your own machine.&lt;/p&gt;

&lt;p&gt;The key distinction is &lt;em&gt;when&lt;/em&gt; the logic runs relative to the request:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Sooket&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;n8n / Make / Zapier&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Execution model&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Inline / synchronous&lt;/strong&gt; — in the HTTP request path&lt;/td&gt;
&lt;td&gt;Async / background, event-triggered&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Shapes the caller's response?&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Yes&lt;/strong&gt; — returns the graph's output&lt;/td&gt;
&lt;td&gt;No — fires after the fact&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Primary use&lt;/td&gt;
&lt;td&gt;API middleware (auth, rate limit, redact, cache, LLM)&lt;/td&gt;
&lt;td&gt;Workflow automation between SaaS apps&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hosting&lt;/td&gt;
&lt;td&gt;Self-hosted single process + SQLite&lt;/td&gt;
&lt;td&gt;Mostly cloud / hosted runners&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;They solve different problems. Reach for an automation tool when you're wiring SaaS events together in the background. Reach for Sooket when something needs to happen &lt;em&gt;during&lt;/em&gt; a request and return a response.&lt;/p&gt;

&lt;h2&gt;
  
  
  A concrete example: the API Guard
&lt;/h2&gt;

&lt;p&gt;A fresh install ships with one workflow already on the canvas — &lt;strong&gt;API Guard&lt;/strong&gt; — so there's something to open and inspect on first run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Input -&amp;gt; Rate Limiter -&amp;gt; PII Redact -&amp;gt; HTTP Request -&amp;gt; Output
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It puts rate limiting and PII redaction in front of an upstream API &lt;em&gt;without touching that API's code&lt;/em&gt;. Callers over the limit get rejected before any work happens, PII is scrubbed before the request is forwarded, and the upstream response comes back through the same endpoint.&lt;/p&gt;

&lt;p&gt;It ships &lt;strong&gt;inactive&lt;/strong&gt; with a placeholder upstream URL. To run it for real: point the HTTP Request node at your API, toggle the workflow active, and call it with its per-workflow key:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://localhost:3000/api/v1/chat &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer sk-wf-xxxxxxxxxxxx"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"email": "ada@example.com", "note": "call me at 555-123-4567"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Quickstart (about 30 seconds)
&lt;/h2&gt;

&lt;p&gt;Requires &lt;strong&gt;Node ≥ 22&lt;/strong&gt; (it uses the built-in &lt;code&gt;node:sqlite&lt;/code&gt;). Check with &lt;code&gt;node -v&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx sooket          &lt;span class="c"&gt;# → http://localhost:3000&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Data — the SQLite database plus a generated encryption secret — lives in &lt;code&gt;~/.sooket&lt;/code&gt; (override with &lt;code&gt;SOOKET_DATA_DIR&lt;/code&gt;). Or run it from source / via Docker Compose if you'd rather. Then open the canvas, build a workflow, and mint an &lt;code&gt;sk-wf-*&lt;/code&gt; key for it from the workflow's settings.&lt;/p&gt;
&lt;h2&gt;
  
  
  What's actually in the box (v0.1.1)
&lt;/h2&gt;

&lt;p&gt;Verified and shipping today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Visual canvas&lt;/strong&gt; — a React Flow editor with auto-insert-into-edge, per-node config panels, and live sandbox testing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;45 nodes&lt;/strong&gt; across AI, Request, External, Format, Logic, Transform, and Static families.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inline execution&lt;/strong&gt; — &lt;code&gt;POST /api/v1/chat&lt;/code&gt; runs the graph synchronously and returns the result.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Webhooks&lt;/strong&gt; — a token-gated &lt;code&gt;/api/webhooks/[slug]&lt;/code&gt; endpoint per workflow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per-workflow API keys&lt;/strong&gt; — &lt;code&gt;sk-wf-*&lt;/code&gt; keys with scopes, rate-limit overrides, expiry, and 30-day usage stats.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encrypted secrets&lt;/strong&gt; — AES-GCM + PBKDF2 for provider keys, credentials, and variables, encrypted at rest.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Versioning&lt;/strong&gt; — every save snapshots nodes/edges (capped at 50); restore any previous version.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observability&lt;/strong&gt; — per-request and per-node execution logs with paginated history.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Standalone runtime&lt;/strong&gt; — an optional execution server that shares the same SQLite file.
A few of the 45 nodes worth calling out: Rate Limiter, PII Redact, Content Guardrail, Cache, &lt;strong&gt;Semantic Cache&lt;/strong&gt;, Router, A/B Split, Retry, Try/Catch, Vector Upsert/Search, Sub-Workflow, and a Custom Code node for when you need an escape hatch.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  How it works under the hood
&lt;/h2&gt;

&lt;p&gt;A workflow is stored as &lt;code&gt;nodes&lt;/code&gt; + &lt;code&gt;edges&lt;/code&gt; JSON in SQLite. The request flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A caller sends &lt;code&gt;POST /api/v1/chat&lt;/code&gt; with a &lt;code&gt;Bearer sk-wf-*&lt;/code&gt; key.&lt;/li&gt;
&lt;li&gt;Sooket resolves the key to its workflow.&lt;/li&gt;
&lt;li&gt;The engine walks the node graph recursively (rate limit, redact, HTTP, LLM, …), memoising each handle so it's computed once per request.&lt;/li&gt;
&lt;li&gt;The Output node's result is returned to the caller in the same response.
The engine lives in &lt;code&gt;lib/workflow-engine.ts&lt;/code&gt;; node executors are registered by type + version in &lt;code&gt;lib/nodes/registry.ts&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Being honest about where it is
&lt;/h2&gt;

&lt;p&gt;Sooket is early — this is &lt;strong&gt;v0.1.1&lt;/strong&gt;. A couple of things worth knowing before you wire it into anything serious:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Security model is deliberate, not accidental.&lt;/strong&gt; The management API has &lt;em&gt;no per-user login&lt;/em&gt; and binds to &lt;code&gt;127.0.0.1&lt;/code&gt; only. To expose it, set a &lt;code&gt;SOOKET_AUTH_TOKEN&lt;/code&gt; shared secret or front it with an authenticating reverse proxy + TLS. Don't just widen the bind address and walk away.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It adds to request latency, because it &lt;em&gt;is&lt;/em&gt; the request.&lt;/strong&gt; That's the trade-off of inline execution — worth it for guard/redact/cache logic, something to measure for hot paths.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Per-node reference docs and examples are still being written.&lt;/strong&gt;
## License&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Source-available under the &lt;strong&gt;Functional Source License, Version 1.1, MIT Future License&lt;/strong&gt; (&lt;code&gt;FSL-1.1-MIT&lt;/code&gt;): free to use, modify, and self-host for any purpose &lt;em&gt;except&lt;/em&gt; offering it as a competing commercial product — and each release automatically becomes MIT-licensed two years after it ships.&lt;/p&gt;
&lt;h2&gt;
  
  
  Try it / tell me where it breaks
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx sooket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Repo, docs, and architecture notes: &lt;strong&gt;&lt;a href="https://github.com/danilopatrial/sooket" rel="noopener noreferrer"&gt;github.com/danilopatrial/sooket&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/danilopatrial" rel="noopener noreferrer"&gt;
        danilopatrial
      &lt;/a&gt; / &lt;a href="https://github.com/danilopatrial/sooket" rel="noopener noreferrer"&gt;
        sooket
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
       Visual canvas for API orchestration. Design middleware flows between your services, transform payloads, and ship to edge runtime. 
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Sooket&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Build API middleware on a canvas — and run it inline in the request path.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/danilopatrial/sooket/LICENSE.md" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/1bd2d5ba377a5e424fc092a685b3981f9ede42e281c3c79ca216a20c5c858691/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d46534c2d2d312e312d2d4d49542d626c75652e737667" alt="License: FSL-1.1-MIT"&gt;&lt;/a&gt;
&lt;a href="https://github.com/danilopatrial/sooket/actions/workflows/test.yml" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/danilopatrial/sooket/actions/workflows/test.yml/badge.svg" alt="CI"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/b5c951521e9a469eba0c4bb091a39c409c5828ee03d78f5212458b80e38a182d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f76657273696f6e2d302e312e312d6f72616e67652e737667"&gt;&lt;img src="https://camo.githubusercontent.com/b5c951521e9a469eba0c4bb091a39c409c5828ee03d78f5212458b80e38a182d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f76657273696f6e2d302e312e312d6f72616e67652e737667" alt="version"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Sooket is a visual canvas for building the logic that sits &lt;em&gt;between&lt;/em&gt; a caller and
your API — rate limiting, auth checks, PII redaction, caching, LLM calls
request/response shaping — without writing a service to host it. You drag nodes
connect them, and expose the pipeline as a single HTTP endpoint. Every request
runs through the graph &lt;strong&gt;synchronously&lt;/strong&gt; and gets a response back.&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/danilopatrial/sooket/docs/img.jpg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fdanilopatrial%2Fsooket%2FHEAD%2Fdocs%2Fimg.jpg" alt="The API Guard example workflow on the Sooket canvas"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Every API eventually needs a layer of glue in front of it: throttle abusive
callers, strip PII before it hits a third party, cache expensive responses
validate a token, fan a request out to an LLM. Today that glue is either
hand-written middleware you have to build, test, and deploy, or it lives in an
automation tool like n8n/Zapier — which run &lt;em&gt;asynchronously&lt;/em&gt;, after the fact, and
can't shape…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/danilopatrial/sooket" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;If you build something with it — or if it falls over on your setup — I'd genuinely like to hear about it in the comments. The "inline middleware canvas" space is pretty empty, and I'm figuring out the sharp edges in the open.&lt;/p&gt;

</description>
      <category>api</category>
      <category>middleware</category>
    </item>
  </channel>
</rss>
