<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>W7S Blog</title>
        <link>https://w7s.io/docs/blog/</link>
        <description>W7S Blog</description>
        <lastBuildDate>Sun, 31 May 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[Coolify vs W7S]]></title>
            <link>https://w7s.io/docs/blog/coolify-vs-w7s/</link>
            <guid>https://w7s.io/docs/blog/coolify-vs-w7s/</guid>
            <pubDate>Sun, 31 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A practical comparison of Coolify and W7S for teams choosing between self-hosted server management and GitHub-native app deployment.]]></description>
            <content:encoded><![CDATA[<p>Coolify and W7S solve different platform problems.</p>
<p>Coolify helps you deploy apps, databases, and services on infrastructure you bring. It is attractive when you want a self-hosted platform experience over your own servers.</p>
<p>W7S helps you deploy repository-shaped apps through GitHub Actions. It is attractive when you want static assets, native backends, storage, queues, schedules, workflows, branch environments, and app URLs without asking every project to manage servers.</p>
<p>The short version:</p>
<blockquote>
<p>Choose Coolify when you want to operate your own servers. Choose W7S when you want GitHub-native app deployment and managed platform primitives.</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="coolify-vs-w7s">Coolify vs W7S<a href="https://w7s.io/docs/blog/coolify-vs-w7s/#coolify-vs-w7s" class="hash-link" aria-label="Direct link to Coolify vs W7S" title="Direct link to Coolify vs W7S" translate="no">​</a></h2>
<table><thead><tr><th>Question</th><th>Coolify</th><th>W7S</th></tr></thead><tbody><tr><td>What is it?</td><td>Self-hosted or managed control plane for deploying to your servers</td><td>GitHub-native deployment cloud for repo-shaped apps</td></tr><tr><td>Who brings infrastructure?</td><td>You bring servers for deployed apps</td><td><code>w7s.cloud</code> provides hosted runtime, or you self-host W7S as a platform</td></tr><tr><td>Main unit</td><td>App, service, database, server</td><td>GitHub owner/repo environment</td></tr><tr><td>Runtime model</td><td>Containers and services on connected servers</td><td>Static assets plus JavaScript/TypeScript native backends and bindings</td></tr><tr><td>Deployment control</td><td>Coolify dashboard and integrations</td><td>GitHub Actions workflow</td></tr><tr><td>Storage</td><td>Databases and services you deploy/manage</td><td>W7S DB, KV, FS, queues, workflows, Stateful Objects, and external bindings</td></tr><tr><td>Best fit</td><td>Server owners, homelabs, agencies, teams that want PaaS on their own machines</td><td>Teams that want repos to deploy without server management</td></tr><tr><td>Main tradeoff</td><td>You own server operations</td><td>Narrower runtime model than general server/container hosting</td></tr></tbody></table>
<p>These tools are not interchangeable clones. They sit at different layers.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="source-backed-comparison-points">Source-Backed Comparison Points<a href="https://w7s.io/docs/blog/coolify-vs-w7s/#source-backed-comparison-points" class="hash-link" aria-label="Direct link to Source-Backed Comparison Points" title="Direct link to Source-Backed Comparison Points" translate="no">​</a></h2>
<p>The Coolify side of this comparison is based on the live <a href="https://coolify.io/pricing/" target="_blank" rel="noopener noreferrer" class="">Coolify pricing page</a> and Coolify docs for <a href="https://coolify.io/docs/applications/" target="_blank" rel="noopener noreferrer" class="">applications</a>, <a href="https://coolify.io/docs/services/introduction" target="_blank" rel="noopener noreferrer" class="">services</a>, and <a href="https://coolify.io/docs/knowledge-base/server/introduction" target="_blank" rel="noopener noreferrer" class="">servers</a>. Those pages back the article's claim that Coolify is organized around apps and services running on connected infrastructure.</p>
<p>The W7S side is based on <a class="" href="https://w7s.io/docs/deploy-from-github/">Deploy From GitHub</a>, <a class="" href="https://w7s.io/docs/project-layouts/">Project Layouts</a>, <a class="" href="https://w7s.io/docs/storage-bindings/">Storage Bindings</a>, and <a class="" href="https://w7s.io/docs/urls-and-routing/">URLs and Routing</a>. Those pages back the claim that W7S starts from the repository and deploys app-shaped projects through GitHub Actions.</p>
<p>The self-hosting distinction is backed by Coolify's own pricing and server docs, plus <a class="" href="https://w7s.io/docs/self-host/">Self Host W7S</a>. Coolify self-hosting means running a control plane for infrastructure you bring. W7S self-hosting means running the W7S deployment cloud while app repositories keep the same GitHub-native deployment contract.</p>
<p>The backend-platform comparison is supported by W7S docs for <a class="" href="https://w7s.io/docs/serverless-database/">Serverless Database</a>, <a class="" href="https://w7s.io/docs/backend-queues/">Backend Queues</a>, <a class="" href="https://w7s.io/docs/backend-schedules/">Backend Schedules</a>, <a class="" href="https://w7s.io/docs/backend-workflows/">Backend Workflows</a>, and <a class="" href="https://w7s.io/docs/backend-rpc/">Backend RPC</a>. Those primitives are why W7S can be a real alternative for app-shaped projects that do not need arbitrary containers.</p>
<p>The cost and operations claims use <a href="https://coolify.io/pricing/" target="_blank" rel="noopener noreferrer" class="">Coolify pricing</a>, <a class="" href="https://w7s.io/docs/pricing/">W7S pricing</a>, and <a class="" href="https://w7s.io/docs/usage-accounting/">W7S Usage Accounting</a>. The article links directly to those pages because server costs, platform prices, and included usage limits can change.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-coolify-is-best-at">What Coolify Is Best At<a href="https://w7s.io/docs/blog/coolify-vs-w7s/#what-coolify-is-best-at" class="hash-link" aria-label="Direct link to What Coolify Is Best At" title="Direct link to What Coolify Is Best At" translate="no">​</a></h2>
<p>Coolify is strongest when the operating model starts with servers you control.</p>
<p>Its pricing page currently describes self-hosting as free forever on your own infrastructure, with full access to features and community support. It also lists Coolify Cloud as a managed Coolify control plane with a base monthly price for connected servers, plus a monthly price per additional server. The important detail is that you still bring the servers where apps run.</p>
<p>That makes Coolify a good fit for:</p>
<ul>
<li class="">VPS-based hosting;</li>
<li class="">self-hosted apps;</li>
<li class="">Docker and service deployments;</li>
<li class="">databases you want to run yourself;</li>
<li class="">homelab and agency workflows;</li>
<li class="">teams that want a Heroku-like layer over machines they control;</li>
<li class="">apps that need container/process flexibility.</li>
</ul>
<p>Coolify is the better choice when server ownership is the goal.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-w7s-is-best-at">What W7S Is Best At<a href="https://w7s.io/docs/blog/coolify-vs-w7s/#what-w7s-is-best-at" class="hash-link" aria-label="Direct link to What W7S Is Best At" title="Direct link to What W7S Is Best At" translate="no">​</a></h2>
<p>W7S is strongest when the operating model starts with a GitHub repository.</p>
<p>The app deploys from GitHub Actions:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">.github/workflows/deploy.yml</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Deploy</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">on</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">push</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">branches</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">main</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">jobs</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">deploy</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">runs-on</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> ubuntu</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">latest</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">permissions</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">contents</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> read</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">id-token</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> write</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">steps</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> actions/checkout@v5</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> actions/setup</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">node@v6</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token key atrule">node-version</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">22</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> npm ci</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> npm run build</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> w7s</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">io/w7s</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">cloud@v1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token key atrule">token</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> github.token </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The runtime contract lives in <code>w7s.json</code>:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"bindings"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"db"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"binding"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"DB"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"migrations"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"migrations"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"kv"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"CACHE"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"fs"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"FILES"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"queues"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"jobs"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"schedules"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"cron"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"0 * * * *"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"path"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/_w7s/schedules/hourly"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"workflows"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"checkout"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>That makes W7S a better fit for:</p>
<ul>
<li class="">static sites that grow into apps;</li>
<li class="">JavaScript and TypeScript native backends;</li>
<li class="">repo-declared DB, KV, FS, queues, schedules, and workflows;</li>
<li class="">branch environments;</li>
<li class="">internal backend RPC by repository identity;</li>
<li class="">GitHub-native usage warnings;</li>
<li class="">teams that want a self-hostable platform path without making every app team manage servers.</li>
</ul>
<p>W7S is the better choice when the repository should be the unit of deployment.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-self-hosting-difference">The Self-Hosting Difference<a href="https://w7s.io/docs/blog/coolify-vs-w7s/#the-self-hosting-difference" class="hash-link" aria-label="Direct link to The Self-Hosting Difference" title="Direct link to The Self-Hosting Difference" translate="no">​</a></h2>
<p>Both products can be discussed as self-hostable, but the meaning is different.</p>
<p>With Coolify, self-hosting usually means:</p>
<ul>
<li class="">you run Coolify;</li>
<li class="">you connect servers;</li>
<li class="">your apps deploy to those servers;</li>
<li class="">you manage server capacity, OS updates, Docker behavior, database backups, disks, network rules, and service health.</li>
</ul>
<p>With W7S, self-hosting means:</p>
<ul>
<li class="">you run the W7S deployment cloud under your own domain and infrastructure account;</li>
<li class="">app repositories still deploy through the W7S GitHub Action;</li>
<li class="">apps still use owner/repo-derived URLs and branch environments;</li>
<li class="">the platform continues to expose W7S primitives instead of turning every app into server management.</li>
</ul>
<p>Coolify gives you a control panel for your servers. W7S gives you an app platform that can be hosted by you.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="runtime-shape-matters">Runtime Shape Matters<a href="https://w7s.io/docs/blog/coolify-vs-w7s/#runtime-shape-matters" class="hash-link" aria-label="Direct link to Runtime Shape Matters" title="Direct link to Runtime Shape Matters" translate="no">​</a></h2>
<p>Coolify supports a broader class of workloads because it is closer to containers and services.</p>
<p>Use Coolify when the app needs:</p>
<ul>
<li class="">arbitrary containers;</li>
<li class="">long-running processes;</li>
<li class="">custom service topology;</li>
<li class="">databases and services you want to run directly;</li>
<li class="">shell/server-level access;</li>
<li class="">private server networks;</li>
<li class="">app stacks that do not fit W7S native backends.</li>
</ul>
<p>W7S intentionally narrows the runtime:</p>
<ul>
<li class="">static assets;</li>
<li class="">JavaScript or TypeScript backend handlers;</li>
<li class="">DB, KV, FS, queues, schedules, workflows, Stateful Objects, and AI bindings;</li>
<li class="">internal RPC;</li>
<li class="">GitHub Actions deploys;</li>
<li class="">branch environments.</li>
</ul>
<p>That narrower model is a feature when the app fits it. It removes server choices from projects that do not need them.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="cost-model-difference">Cost Model Difference<a href="https://w7s.io/docs/blog/coolify-vs-w7s/#cost-model-difference" class="hash-link" aria-label="Direct link to Cost Model Difference" title="Direct link to Cost Model Difference" translate="no">​</a></h2>
<p>Coolify's public pricing makes the infrastructure ownership clear. Self-hosted Coolify is free as software, but you need your own infrastructure. Coolify Cloud charges for the managed control plane while your apps still deploy to servers you bring.</p>
<p>W7S hosted deployment starts free without a W7S account, credit card, or separate cloud setup. When an app gets meaningful traffic, W7S is designed around usage-based cost for app primitives and platform overhead.</p>
<p>So the cost comparison is not just monthly subscription versus monthly subscription.</p>
<p>With Coolify, include:</p>
<ul>
<li class="">Coolify Cloud fee if using it;</li>
<li class="">server rental;</li>
<li class="">database/storage costs;</li>
<li class="">backups;</li>
<li class="">monitoring;</li>
<li class="">time spent operating the machines.</li>
</ul>
<p>With W7S, include:</p>
<ul>
<li class="">app usage;</li>
<li class="">storage and backend primitive usage;</li>
<li class="">platform overhead included in W7S estimates;</li>
<li class="">whether the app outgrows the W7S runtime model.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="when-coolify-is-better">When Coolify Is Better<a href="https://w7s.io/docs/blog/coolify-vs-w7s/#when-coolify-is-better" class="hash-link" aria-label="Direct link to When Coolify Is Better" title="Direct link to When Coolify Is Better" translate="no">​</a></h2>
<p>Choose Coolify when:</p>
<ul>
<li class="">you want to bring your own servers;</li>
<li class="">you want to deploy arbitrary containers;</li>
<li class="">you want to run databases and services yourself;</li>
<li class="">you need process-level control;</li>
<li class="">you are comfortable operating the server layer;</li>
<li class="">your organization values server ownership over a narrower app platform.</li>
</ul>
<p>Coolify is a strong choice for teams that already think in servers and services.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="when-w7s-is-better">When W7S Is Better<a href="https://w7s.io/docs/blog/coolify-vs-w7s/#when-w7s-is-better" class="hash-link" aria-label="Direct link to When W7S Is Better" title="Direct link to When W7S Is Better" translate="no">​</a></h2>
<p>Choose W7S when:</p>
<ul>
<li class="">the app can be static assets plus native backend routes;</li>
<li class="">GitHub Actions should own deploys;</li>
<li class="">the deploy token should be the GitHub token;</li>
<li class="">branch environments should be automatic and repo-scoped;</li>
<li class="">DB, KV, FS, queues, schedules, workflows, vars, and secrets should be declared with the app;</li>
<li class="">internal service calls should use repository identity;</li>
<li class="">you do not want every project to size, patch, monitor, and back up servers;</li>
<li class="">you want the option to self-host the platform later.</li>
</ul>
<p>W7S is better when server management is not part of the product you are building.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="recommendation">Recommendation<a href="https://w7s.io/docs/blog/coolify-vs-w7s/#recommendation" class="hash-link" aria-label="Direct link to Recommendation" title="Direct link to Recommendation" translate="no">​</a></h2>
<p>Do not ask "Coolify or W7S?" first. Ask what you want to own.</p>
<p>If you want to own servers and run a broad set of containers, choose Coolify.</p>
<p>If you want GitHub-native app deployment with a small set of managed primitives, choose W7S.</p>
<p>Both can be good tools. The wrong choice is picking server management when you wanted an app platform, or picking an app platform when the workload really needs server control.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="current-pricing-sources">Current Pricing Sources<a href="https://w7s.io/docs/blog/coolify-vs-w7s/#current-pricing-sources" class="hash-link" aria-label="Direct link to Current Pricing Sources" title="Direct link to Current Pricing Sources" translate="no">​</a></h2>
<p>Pricing changes. Check the current public pages before making a final cost decision:</p>
<ul>
<li class=""><a href="https://coolify.io/pricing/" target="_blank" rel="noopener noreferrer" class="">Coolify pricing</a></li>
<li class=""><a class="" href="https://w7s.io/docs/pricing/">W7S pricing calculator</a></li>
</ul>]]></content:encoded>
            <category>platforms</category>
            <category>self-hosting</category>
            <category>alternatives</category>
            <category>coolify</category>
        </item>
        <item>
            <title><![CDATA[Heroku Alternatives]]></title>
            <link>https://w7s.io/docs/blog/heroku-alternatives/</link>
            <guid>https://w7s.io/docs/blog/heroku-alternatives/</guid>
            <pubDate>Sun, 31 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A practical guide to Heroku alternatives, including W7S, Render, Railway, Fly.io, Cloud Run, App Runner, Kubernetes, Coolify, and when W7S is the better fit.]]></description>
            <content:encoded><![CDATA[<p>The best Heroku alternative depends on what you actually used Heroku for.</p>
<p>If you used Heroku as a simple process host, Render, Railway, Fly.io, Google Cloud Run, AWS App Runner, DigitalOcean App Platform, and Coolify are natural comparisons. If you used Heroku as a full application platform with dynos, add-ons, config vars, logs, and Git deploys, the decision is more subtle.</p>
<p>W7S is a Heroku alternative for a specific and common app shape:</p>
<blockquote>
<p>Use W7S when the app can be static assets, native backend routes, managed bindings, queues, schedules, and workflows instead of always-on processes.</p>
</blockquote>
<p>That makes W7S better for small and medium apps where the process was mostly packaging around request handlers, background jobs, storage, and a deploy workflow from GitHub.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-short-list">The Short List<a href="https://w7s.io/docs/blog/heroku-alternatives/#the-short-list" class="hash-link" aria-label="Direct link to The Short List" title="Direct link to The Short List" translate="no">​</a></h2>
<table><thead><tr><th>Heroku alternative</th><th>Best fit</th><th>Tradeoff</th></tr></thead><tbody><tr><td>W7S</td><td>GitHub-native apps with static assets, native backends, DB, KV, FS, queues, schedules, workflows, and branch environments</td><td>Not an arbitrary container or long-running process platform</td></tr><tr><td>Render</td><td>Web services, background workers, cron jobs, managed databases, and simple process hosting</td><td>More service configuration than W7S needs for edge-native apps</td></tr><tr><td>Railway</td><td>Fast project setup for services and databases</td><td>Dashboard project model remains central</td></tr><tr><td>Fly.io</td><td>Apps that need machines, regions, private networking, and lower-level runtime control</td><td>You operate closer to infrastructure</td></tr><tr><td>Google Cloud Run</td><td>Containerized HTTP services on Google Cloud</td><td>Cloud IAM, container, and project setup become part of the app</td></tr><tr><td>AWS App Runner</td><td>Container or source-based web services on AWS</td><td>AWS account and service wiring come with it</td></tr><tr><td>DigitalOcean App Platform</td><td>Managed app hosting with familiar service concepts</td><td>Still a hosted service model around processes</td></tr><tr><td>Kubernetes</td><td>Teams that need a cluster platform</td><td>Too much operational surface for many small apps</td></tr><tr><td>Coolify</td><td>Self-hosted app and service management on your own servers</td><td>You bring and operate the servers</td></tr></tbody></table>
<p>Heroku is still useful. Its pricing page describes dynos as the heart of a Heroku app, with dyno tiers such as Eco, Basic, Standard, Performance, and larger private-space options. That is a process model: the app runs in isolated containers that handle code, dependencies, and scaling.</p>
<p>W7S competes by changing that model for apps that do not need a long-running process.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="source-backed-comparison-points">Source-Backed Comparison Points<a href="https://w7s.io/docs/blog/heroku-alternatives/#source-backed-comparison-points" class="hash-link" aria-label="Direct link to Source-Backed Comparison Points" title="Direct link to Source-Backed Comparison Points" translate="no">​</a></h2>
<p>Heroku's runtime model is documented in its pages for <a href="https://devcenter.heroku.com/articles/dynos" target="_blank" rel="noopener noreferrer" class="">dynos</a>, the <a href="https://devcenter.heroku.com/articles/procfile" target="_blank" rel="noopener noreferrer" class="">Procfile</a>, <a href="https://devcenter.heroku.com/articles/config-vars" target="_blank" rel="noopener noreferrer" class="">config vars</a>, <a href="https://devcenter.heroku.com/articles/add-ons" target="_blank" rel="noopener noreferrer" class="">add-ons</a>, and <a href="https://devcenter.heroku.com/articles/one-off-dynos" target="_blank" rel="noopener noreferrer" class="">one-off dynos</a>. Those sources back the article's description of Heroku as a process-centered app platform.</p>
<p>The W7S replacement model is backed by the W7S docs for <a class="" href="https://w7s.io/docs/deploy-from-github/">deploying from GitHub</a>, <a class="" href="https://w7s.io/docs/project-layouts/">project layouts</a>, <a class="" href="https://w7s.io/docs/storage-bindings/">storage bindings</a>, <a class="" href="https://w7s.io/docs/serverless-database/">serverless database</a>, and <a class="" href="https://w7s.io/docs/urls-and-routing/">URLs and routing</a>. Those pages show how a repository can own deployment, native backend shape, app URLs, and runtime bindings without a dyno layer.</p>
<p>The background-work comparison is based on the W7S docs for <a class="" href="https://w7s.io/docs/backend-queues/">Backend Queues</a>, <a class="" href="https://w7s.io/docs/backend-schedules/">Backend Schedules</a>, and <a class="" href="https://w7s.io/docs/backend-workflows/">Backend Workflows</a>. Those primitives are the reason W7S can replace worker dynos for common async jobs, scheduled syncs, webhook retries, and durable application processes.</p>
<p>Other Heroku alternatives still matter when an app is process-shaped. Render documents <a href="https://render.com/docs/web-services" target="_blank" rel="noopener noreferrer" class="">web services</a>, <a href="https://render.com/docs/background-workers" target="_blank" rel="noopener noreferrer" class="">background workers</a>, and <a href="https://render.com/docs/cronjobs" target="_blank" rel="noopener noreferrer" class="">cron jobs</a>; Fly.io documents <a href="https://fly.io/docs/machines/" target="_blank" rel="noopener noreferrer" class="">Machines</a>; Google documents <a href="https://docs.cloud.google.com/run/docs" target="_blank" rel="noopener noreferrer" class="">Cloud Run</a>; and AWS documents <a href="https://docs.aws.amazon.com/apprunner/latest/dg/what-is-apprunner.html" target="_blank" rel="noopener noreferrer" class="">App Runner</a>. Those sources back the article's recommendation to keep process platforms for process-shaped workloads.</p>
<p>The cost and ownership claims are tied to <a href="https://www.heroku.com/pricing/" target="_blank" rel="noopener noreferrer" class="">Heroku pricing</a>, the <a class="" href="https://w7s.io/docs/pricing/">W7S pricing calculator</a>, <a class="" href="https://w7s.io/docs/usage-accounting/">W7S usage accounting</a>, and <a class="" href="https://w7s.io/docs/self-host/">self-hosting W7S</a>. Pricing pages change, so the article points readers to the live sources instead of treating any single number as permanent.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-heroku-gives-you">What Heroku Gives You<a href="https://w7s.io/docs/blog/heroku-alternatives/#what-heroku-gives-you" class="hash-link" aria-label="Direct link to What Heroku Gives You" title="Direct link to What Heroku Gives You" translate="no">​</a></h2>
<p>Heroku became popular because it gave developers a simple application surface:</p>
<ul>
<li class="">a <code>Procfile</code>;</li>
<li class="">web and worker dynos;</li>
<li class="">config vars;</li>
<li class="">add-on databases and services;</li>
<li class="">logs and metrics;</li>
<li class="">Git-based deploys;</li>
<li class="">scaling through dyno counts and dyno sizes;</li>
<li class="">release commands and one-off tasks;</li>
<li class="">custom domains and TLS.</li>
</ul>
<p>That is a good shape when the application genuinely needs processes. A Rails app, Django app, Phoenix app, long-running worker, or containerized service can fit that model well.</p>
<p>The cost is that every app becomes something you operate as a set of services. You think about process types, process sizes, worker counts, add-ons, startup commands, health checks, deployment phases, and idle behavior.</p>
<p>For many modern JavaScript and TypeScript apps, that is heavier than necessary.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-w7s-replaces">What W7S Replaces<a href="https://w7s.io/docs/blog/heroku-alternatives/#what-w7s-replaces" class="hash-link" aria-label="Direct link to What W7S Replaces" title="Direct link to What W7S Replaces" translate="no">​</a></h2>
<p>W7S maps the common Heroku platform pieces to repository-native components:</p>
<table><thead><tr><th>Heroku concept</th><th>W7S replacement</th></tr></thead><tbody><tr><td>Git deploy</td><td>GitHub Actions deploy</td></tr><tr><td><code>Procfile</code> web process</td><td>Native backend entrypoint</td></tr><tr><td>Static assets</td><td>Static asset deploy</td></tr><tr><td>Worker dyno</td><td>Queue consumer route</td></tr><tr><td>Scheduler add-on</td><td>W7S schedule</td></tr><tr><td>Release phase</td><td>GitHub Actions step or explicit backend route</td></tr><tr><td>Config vars</td><td><code>w7s.json</code> vars and secrets</td></tr><tr><td>Add-on database</td><td>Serverless DB or Postgres binding</td></tr><tr><td>Redis-style cache</td><td>KV or Stateful Objects</td></tr><tr><td>Object storage add-on</td><td>FS binding</td></tr><tr><td>App URL</td><td>GitHub owner/repo-derived URL</td></tr><tr><td>Review apps</td><td>Branch environments</td></tr></tbody></table>
<p>The point is not to clone Heroku. The point is to remove the process layer when the app does not need it.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="from-procfile-to-native-backend">From Procfile to Native Backend<a href="https://w7s.io/docs/blog/heroku-alternatives/#from-procfile-to-native-backend" class="hash-link" aria-label="Direct link to From Procfile to Native Backend" title="Direct link to From Procfile to Native Backend" translate="no">​</a></h2>
<p>A Heroku app often starts with a <code>Procfile</code>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">Procfile</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">web: npm start</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">worker: npm run worker</span><br></div></code></pre></div></div>
<p>In W7S, the backend is a request handler:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/index.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">async</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token operator">:</span><span class="token plain"> Request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> env</span><span class="token operator">:</span><span class="token plain"> Env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> url </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name constant" style="color:rgb(189, 147, 249)">URL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">pathname </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/api/health"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">ok</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">pathname </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/api/orders"</span><span class="token plain"> </span><span class="token operator">&amp;&amp;</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">method </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"POST"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">createOrder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Not found"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">status</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">404</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>There is no port to bind and no dyno size to choose. W7S owns request dispatch; the app owns the handler code.</p>
<p>That is a better fit when the backend is mostly HTTP routes, validation, auth, database access, file writes, and calls to other app components.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="from-worker-dynos-to-queues">From Worker Dynos to Queues<a href="https://w7s.io/docs/blog/heroku-alternatives/#from-worker-dynos-to-queues" class="hash-link" aria-label="Direct link to From Worker Dynos to Queues" title="Direct link to From Worker Dynos to Queues" translate="no">​</a></h2>
<p>A Heroku worker dyno is a good abstraction when you have a long-running worker process.</p>
<p>Many apps only need async delivery. In W7S, the app declares a queue:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"queues"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"jobs"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"consumer"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/_w7s/queues/jobs"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The backend consumes messages through a route:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/index.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">pathname </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/_w7s/queues/jobs"</span><span class="token plain"> </span><span class="token operator">&amp;&amp;</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">method </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"POST"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> batch </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">for</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> message </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">of</span><span class="token plain"> batch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">messages</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">sendReceipt</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">message</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">body</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">ok</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> processed</span><span class="token operator">:</span><span class="token plain"> batch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">messages</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">length</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>That removes a separate process for jobs that already belong to the app.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="from-add-ons-to-bindings">From Add-Ons to Bindings<a href="https://w7s.io/docs/blog/heroku-alternatives/#from-add-ons-to-bindings" class="hash-link" aria-label="Direct link to From Add-Ons to Bindings" title="Direct link to From Add-Ons to Bindings" translate="no">​</a></h2>
<p>Heroku add-ons are convenient, but they often put the runtime contract across a dashboard, provider account, environment variables, and application code.</p>
<p>W7S puts common resources in <code>w7s.json</code>:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"bindings"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"db"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"binding"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"DB"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"migrations"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"migrations"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"kv"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"CACHE"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"fs"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"FILES"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"queues"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"jobs"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"schedules"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"cron"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"0 * * * *"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"path"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/_w7s/schedules/hourly"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"vars"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"PUBLIC_APP_ORIGIN"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"secrets"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"STRIPE_SECRET_KEY"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The repository explains what the app needs. Branch environments can get isolated resources without copying dashboard settings by hand.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-w7s-can-be-better-than-heroku">Why W7S Can Be Better Than Heroku<a href="https://w7s.io/docs/blog/heroku-alternatives/#why-w7s-can-be-better-than-heroku" class="hash-link" aria-label="Direct link to Why W7S Can Be Better Than Heroku" title="Direct link to Why W7S Can Be Better Than Heroku" translate="no">​</a></h2>
<p>W7S is better than Heroku when these priorities matter more than process control:</p>
<ul>
<li class="">deploy behavior should be reviewable in <code>.github/workflows/deploy.yml</code>;</li>
<li class="">the deploy token should be the GitHub token;</li>
<li class="">static frontend and backend routes should ship together;</li>
<li class="">storage and jobs should be declared with the app;</li>
<li class="">branch previews should include backend and storage behavior;</li>
<li class="">internal backend calls should use repository identity;</li>
<li class="">cost should follow usage instead of an always-on process baseline;</li>
<li class="">the platform should have an open-source and self-hostable path.</li>
</ul>
<p>For a solo builder or small team, the biggest difference is not a feature checklist. It is the number of operational decisions removed from each repo.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="when-heroku-or-a-process-platform-still-wins">When Heroku or a Process Platform Still Wins<a href="https://w7s.io/docs/blog/heroku-alternatives/#when-heroku-or-a-process-platform-still-wins" class="hash-link" aria-label="Direct link to When Heroku or a Process Platform Still Wins" title="Direct link to When Heroku or a Process Platform Still Wins" translate="no">​</a></h2>
<p>Do not replace Heroku with W7S if the app needs:</p>
<ul>
<li class="">arbitrary containers;</li>
<li class="">non-JavaScript runtimes as the main app server;</li>
<li class="">long-running daemon processes;</li>
<li class="">custom TCP listeners;</li>
<li class="">process-level WebSocket control;</li>
<li class="">private networking between long-running services;</li>
<li class="">shell access into running app containers;</li>
<li class="">mature Heroku add-ons your team depends on;</li>
<li class="">a Heroku-specific workflow already standardized across the organization.</li>
</ul>
<p>Render, Railway, Fly.io, Cloud Run, App Runner, DigitalOcean App Platform, Kubernetes, or Coolify may be better if the workload is truly process-shaped.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="recommendation">Recommendation<a href="https://w7s.io/docs/blog/heroku-alternatives/#recommendation" class="hash-link" aria-label="Direct link to Recommendation" title="Direct link to Recommendation" translate="no">​</a></h2>
<p>Start with the runtime shape.</p>
<p>Use W7S when the app is mostly:</p>
<ul>
<li class="">static assets;</li>
<li class="">JavaScript or TypeScript request handlers;</li>
<li class="">serverless DB, KV, FS, queues, schedules, workflows, and Stateful Objects;</li>
<li class="">GitHub Actions deploys;</li>
<li class="">branch environments;</li>
<li class="">repo-owned runtime configuration.</li>
</ul>
<p>Use Heroku or a Heroku-like process platform when the app really needs always-on processes.</p>
<p>The mistake is not choosing Heroku. The mistake is keeping a process platform just because the app used to need one.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="current-pricing-sources">Current Pricing Sources<a href="https://w7s.io/docs/blog/heroku-alternatives/#current-pricing-sources" class="hash-link" aria-label="Direct link to Current Pricing Sources" title="Direct link to Current Pricing Sources" translate="no">​</a></h2>
<p>Pricing changes. Check the current public pages before making a final cost decision:</p>
<ul>
<li class=""><a href="https://www.heroku.com/pricing/" target="_blank" rel="noopener noreferrer" class="">Heroku pricing</a></li>
<li class=""><a class="" href="https://w7s.io/docs/pricing/">W7S pricing calculator</a></li>
</ul>]]></content:encoded>
            <category>platforms</category>
            <category>alternatives</category>
            <category>heroku</category>
            <category>github-actions</category>
        </item>
        <item>
            <title><![CDATA[Netlify Alternative]]></title>
            <link>https://w7s.io/docs/blog/netlify-alternative/</link>
            <guid>https://w7s.io/docs/blog/netlify-alternative/</guid>
            <pubDate>Sun, 31 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Why W7S is a strong Netlify alternative for teams that want GitHub Actions, native backends, storage, queues, schedules, workflows, and branch environments in one repo.]]></description>
            <content:encoded><![CDATA[<p>The best Netlify alternative is not always another frontend platform.</p>
<p>Netlify is strong when you want a polished hosted workflow for frontend projects: Git-connected deploys, branch previews, functions, edge features, forms, a dashboard, team collaboration, analytics, and a growing set of platform services.</p>
<p>W7S is different:</p>
<blockquote>
<p>W7S is the Netlify alternative for teams that want GitHub Actions and the repository to own the deployment contract.</p>
</blockquote>
<p>That makes W7S better when the site has become an app and the app needs backend routes, storage, queues, schedules, workflows, internal service calls, and predictable branch environments without moving the source of truth into a dashboard.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="netlify-vs-w7s">Netlify vs W7S<a href="https://w7s.io/docs/blog/netlify-alternative/#netlify-vs-w7s" class="hash-link" aria-label="Direct link to Netlify vs W7S" title="Direct link to Netlify vs W7S" translate="no">​</a></h2>
<table><thead><tr><th>Need</th><th>Netlify</th><th>W7S</th></tr></thead><tbody><tr><td>Static frontend deploys</td><td>Hosted frontend platform</td><td>GitHub Actions deploy to W7S</td></tr><tr><td>Build settings</td><td>Netlify project settings and config</td><td>Workflow file in the repo</td></tr><tr><td>Preview deploys</td><td>Deploy previews and branch deploys</td><td>Branch environments with repo-scoped URLs</td></tr><tr><td>Functions</td><td>Netlify Functions and Edge Functions</td><td>Native backend entrypoint</td></tr><tr><td>Forms</td><td>Netlify Forms</td><td>Backend route plus DB, KV, queue, or workflow</td></tr><tr><td>Scheduled work</td><td>Scheduled Functions</td><td>W7S schedules</td></tr><tr><td>Storage</td><td>Netlify platform services and integrations</td><td>DB, KV, FS, Stateful Objects</td></tr><tr><td>Background work</td><td>Functions, scheduled work, integrations</td><td>Queues and workflows</td></tr><tr><td>Runtime config</td><td>Dashboard and environment variables</td><td><code>w7s.json</code>, vars, and secrets</td></tr><tr><td>Control plane</td><td>Netlify dashboard plus Git integration</td><td>GitHub Actions plus W7S deploy API</td></tr></tbody></table>
<p>Netlify remains a good fit when the dashboard workflow, visual collaboration, forms product, plugins, and frontend platform surface are central to the team.</p>
<p>W7S is better when those features are less important than keeping deploys, runtime shape, and app resources visible in the repository.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="source-backed-comparison-points">Source-Backed Comparison Points<a href="https://w7s.io/docs/blog/netlify-alternative/#source-backed-comparison-points" class="hash-link" aria-label="Direct link to Source-Backed Comparison Points" title="Direct link to Source-Backed Comparison Points" translate="no">​</a></h2>
<p>Netlify's frontend platform strengths are documented in its pages for <a href="https://docs.netlify.com/deploy/deploy-types/deploy-previews/" target="_blank" rel="noopener noreferrer" class="">Deploy Previews</a>, <a href="https://docs.netlify.com/build/functions/overview/" target="_blank" rel="noopener noreferrer" class="">Functions</a>, <a href="https://docs.netlify.com/build/edge-functions/overview/" target="_blank" rel="noopener noreferrer" class="">Edge Functions</a>, <a href="https://docs.netlify.com/manage/forms/setup/" target="_blank" rel="noopener noreferrer" class="">Forms</a>, and <a href="https://docs.netlify.com/build/functions/scheduled-functions/" target="_blank" rel="noopener noreferrer" class="">Scheduled Functions</a>. Those sources back the article's claim that Netlify remains a strong frontend workflow product.</p>
<p>The W7S deployment comparison is backed by <a class="" href="https://w7s.io/docs/deploy-from-github/">Deploy From GitHub</a>, <a class="" href="https://w7s.io/docs/project-layouts/">Project Layouts</a>, and <a class="" href="https://w7s.io/docs/urls-and-routing/">URLs and Routing</a>. Those pages show how build and deploy behavior can live in GitHub Actions while the app receives predictable owner/repo and branch URLs.</p>
<p>The backend comparison is backed by <a class="" href="https://w7s.io/docs/storage-bindings/">Storage Bindings</a>, <a class="" href="https://w7s.io/docs/serverless-database/">Serverless Database</a>, <a class="" href="https://w7s.io/docs/backend-queues/">Backend Queues</a>, <a class="" href="https://w7s.io/docs/backend-schedules/">Backend Schedules</a>, <a class="" href="https://w7s.io/docs/backend-workflows/">Backend Workflows</a>, and <a class="" href="https://w7s.io/docs/backend-rpc/">Backend RPC</a>. Those W7S docs are the reason this article describes W7S as an app platform alternative rather than just another static site host.</p>
<p>The preview-environment claim is supported by the combination of Netlify's <a href="https://docs.netlify.com/deploy/deploy-types/deploy-previews/" target="_blank" rel="noopener noreferrer" class="">Deploy Preview</a> model and W7S docs for <a class="" href="https://w7s.io/docs/urls-and-routing/">URLs and Routing</a> plus <a class="" href="https://w7s.io/docs/storage-bindings/">Storage Bindings</a>. Netlify proves the value of branch previews; W7S extends that idea to branch-scoped runtime behavior for apps that need backend resources.</p>
<p>The pricing and ownership claims use <a href="https://www.netlify.com/pricing/" target="_blank" rel="noopener noreferrer" class="">Netlify pricing</a>, <a class="" href="https://w7s.io/docs/pricing/">W7S pricing</a>, <a class="" href="https://w7s.io/docs/usage-accounting/">Usage Accounting</a>, and <a class="" href="https://w7s.io/docs/self-host/">Self Host W7S</a>. These links are included because pricing and platform limits change over time.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-teams-look-for-a-netlify-alternative">Why Teams Look for a Netlify Alternative<a href="https://w7s.io/docs/blog/netlify-alternative/#why-teams-look-for-a-netlify-alternative" class="hash-link" aria-label="Direct link to Why Teams Look for a Netlify Alternative" title="Direct link to Why Teams Look for a Netlify Alternative" translate="no">​</a></h2>
<p>Teams usually start looking beyond Netlify for one of five reasons:</p>
<ol>
<li class="">The site needs more backend behavior than a few isolated functions.</li>
<li class="">Storage and background jobs are becoming part of the product.</li>
<li class="">The team wants build and deploy behavior reviewed in pull requests.</li>
<li class="">Pricing needs to map more directly to app usage.</li>
<li class="">The app should have a self-hostable or open-source platform path.</li>
</ol>
<p>These are not Netlify failures. They are signs that a static site has become an application.</p>
<p>W7S is built for that moment.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="deploy-from-github-actions">Deploy From GitHub Actions<a href="https://w7s.io/docs/blog/netlify-alternative/#deploy-from-github-actions" class="hash-link" aria-label="Direct link to Deploy From GitHub Actions" title="Direct link to Deploy From GitHub Actions" translate="no">​</a></h2>
<p>Netlify's default workflow is to connect a repository to a Netlify project. That project builds and deploys pushes and previews.</p>
<p>W7S moves the release logic into GitHub Actions:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">.github/workflows/deploy.yml</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Deploy</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">on</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">push</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">branches</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">main</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">jobs</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">deploy</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">runs-on</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> ubuntu</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">latest</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">permissions</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">contents</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> read</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">id-token</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> write</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">steps</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> actions/checkout@v5</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> actions/setup</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">node@v6</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token key atrule">node-version</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">22</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> npm ci</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> npm run build</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> w7s</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">io/w7s</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">cloud@v1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token key atrule">token</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> github.token </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The workflow can run tests, generate assets, build multiple packages, run migrations, package a specific directory, and deploy only after the checks you choose.</p>
<p>The practical win is reviewability. If the deploy command changes, the pull request shows it.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="replace-functions-with-a-native-backend">Replace Functions With a Native Backend<a href="https://w7s.io/docs/blog/netlify-alternative/#replace-functions-with-a-native-backend" class="hash-link" aria-label="Direct link to Replace Functions With a Native Backend" title="Direct link to Replace Functions With a Native Backend" translate="no">​</a></h2>
<p>File-per-function routing is convenient at first. It gets less convenient when the app needs shared auth, validation, error handling, logging, database helpers, queue helpers, and internal RPC.</p>
<p>W7S uses a native backend:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/index.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">async</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token operator">:</span><span class="token plain"> Request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> env</span><span class="token operator">:</span><span class="token plain"> Env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> ctx</span><span class="token operator">:</span><span class="token plain"> ExecutionContext</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> url </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name constant" style="color:rgb(189, 147, 249)">URL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">pathname </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/api/health"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">ok</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">pathname </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/api/contact"</span><span class="token plain"> </span><span class="token operator">&amp;&amp;</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">method </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"POST"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">submitContactForm</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">pathname </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/api/orders"</span><span class="token plain"> </span><span class="token operator">&amp;&amp;</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">method </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"POST"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">createOrder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> ctx</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Not found"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">status</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">404</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>You can still keep route modules if you want. The difference is that the backend is one service-shaped entrypoint instead of a platform-specific folder convention.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="replace-forms-with-app-code">Replace Forms With App Code<a href="https://w7s.io/docs/blog/netlify-alternative/#replace-forms-with-app-code" class="hash-link" aria-label="Direct link to Replace Forms With App Code" title="Direct link to Replace Forms With App Code" translate="no">​</a></h2>
<p>Netlify Forms are useful for simple marketing and contact flows.</p>
<p>W7S does not try to clone that product feature. It gives you the primitives to implement the flow in application code:</p>
<ul>
<li class="">validate the submitted body in a backend route;</li>
<li class="">write the record to DB or KV;</li>
<li class="">store files in FS;</li>
<li class="">enqueue notification work;</li>
<li class="">start a workflow when the form needs approval or retries;</li>
<li class="">keep spam filtering and business logic in code.</li>
</ul>
<p>That is better when the form is not just a form anymore. Lead routing, account creation, checkout, waitlists, uploads, support intake, and moderation queues usually become app behavior.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="declare-storage-and-jobs-with-the-app">Declare Storage and Jobs With the App<a href="https://w7s.io/docs/blog/netlify-alternative/#declare-storage-and-jobs-with-the-app" class="hash-link" aria-label="Direct link to Declare Storage and Jobs With the App" title="Direct link to Declare Storage and Jobs With the App" translate="no">​</a></h2>
<p>A Netlify project can use environment variables, functions, build plugins, and external services. W7S pushes the app runtime contract into one manifest:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"bindings"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"db"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"binding"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"DB"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"migrations"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"migrations"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"kv"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"CACHE"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"fs"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"FILES"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"queues"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"mail"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"consumer"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/_w7s/queues/mail"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"schedules"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"cron"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"*/15 * * * *"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"path"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/_w7s/schedules/sync"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"workflows"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"checkout"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"vars"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"PUBLIC_APP_ORIGIN"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"secrets"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"RESEND_API_KEY"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>That makes the repository the document for how the app runs.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="branch-environments-are-more-than-previews">Branch Environments Are More Than Previews<a href="https://w7s.io/docs/blog/netlify-alternative/#branch-environments-are-more-than-previews" class="hash-link" aria-label="Direct link to Branch Environments Are More Than Previews" title="Direct link to Branch Environments Are More Than Previews" translate="no">​</a></h2>
<p>Preview deploys are useful because reviewers can open the branch.</p>
<p>Branch environments are more useful because the branch can test runtime behavior:</p>
<ul>
<li class="">backend routes;</li>
<li class="">database migrations;</li>
<li class="">KV and FS usage;</li>
<li class="">queues and schedules;</li>
<li class="">internal RPC;</li>
<li class="">workflows;</li>
<li class="">vars and secrets scoped to the environment.</li>
</ul>
<p>W7S turns branch names into environment names and derives predictable URLs from the GitHub owner and repository. A review app can be more than a rendered frontend.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="pricing-model-difference">Pricing Model Difference<a href="https://w7s.io/docs/blog/netlify-alternative/#pricing-model-difference" class="hash-link" aria-label="Direct link to Pricing Model Difference" title="Direct link to Pricing Model Difference" translate="no">​</a></h2>
<p>Netlify's current public pricing is credit-based. Its pricing page lists a Free plan with a monthly credit limit, Personal and Pro monthly plans, Enterprise custom pricing, and credit usage for production deploys, compute, bandwidth, web requests, and other platform work.</p>
<p>W7S is free to start on <code>w7s.cloud</code> without a W7S account, credit card, or separate cloud setup. The hosted service is designed so small apps can deploy, test, demo, and share before billing matters. When apps get meaningful traffic, the W7S model is intended to be usage-based instead of a mandatory subscription just to keep an app online.</p>
<p>The difference is not only price. It is what gets priced. W7S tries to keep the pricing conversation aligned with app primitives: runtime requests, CPU, storage, DB rows, queues, stateful work, logs, and W7S platform overhead.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="when-netlify-still-wins">When Netlify Still Wins<a href="https://w7s.io/docs/blog/netlify-alternative/#when-netlify-still-wins" class="hash-link" aria-label="Direct link to When Netlify Still Wins" title="Direct link to When Netlify Still Wins" translate="no">​</a></h2>
<p>Keep Netlify when you need:</p>
<ul>
<li class="">Netlify Forms as a product feature;</li>
<li class="">Netlify's dashboard and team workflow;</li>
<li class="">visual collaboration and preview tooling;</li>
<li class="">plugins and extensions your build depends on;</li>
<li class="">Netlify-specific edge or function behavior;</li>
<li class="">organization policy already built around Netlify;</li>
<li class="">a mature frontend platform with less custom CI setup.</li>
</ul>
<p>Use W7S when the repository should own the deployment and the app can fit W7S's runtime model.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="recommendation">Recommendation<a href="https://w7s.io/docs/blog/netlify-alternative/#recommendation" class="hash-link" aria-label="Direct link to Recommendation" title="Direct link to Recommendation" translate="no">​</a></h2>
<p>Choose W7S as a Netlify alternative when the project can be described as:</p>
<ul>
<li class="">static assets built in GitHub Actions;</li>
<li class="">JavaScript or TypeScript backend routes;</li>
<li class="">repo-declared DB, KV, FS, queues, schedules, workflows, vars, and secrets;</li>
<li class="">branch environments for runtime previews;</li>
<li class="">GitHub as the release control plane.</li>
</ul>
<p>Choose Netlify when its hosted frontend product is the workflow you want.</p>
<p>The practical question is simple: should this app be managed from a dashboard, or should the repository explain how it deploys and runs?</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="current-pricing-sources">Current Pricing Sources<a href="https://w7s.io/docs/blog/netlify-alternative/#current-pricing-sources" class="hash-link" aria-label="Direct link to Current Pricing Sources" title="Direct link to Current Pricing Sources" translate="no">​</a></h2>
<p>Pricing changes. Check the current public pages before making a final cost decision:</p>
<ul>
<li class=""><a href="https://www.netlify.com/pricing/" target="_blank" rel="noopener noreferrer" class="">Netlify pricing</a></li>
<li class=""><a class="" href="https://w7s.io/docs/pricing/">W7S pricing calculator</a></li>
</ul>]]></content:encoded>
            <category>platforms</category>
            <category>alternatives</category>
            <category>netlify</category>
            <category>backends</category>
        </item>
        <item>
            <title><![CDATA[Replacing Raw Cloudflare Workers Setup With W7S]]></title>
            <link>https://w7s.io/docs/blog/replacing-cloudflare-workers-with-w7s/</link>
            <guid>https://w7s.io/docs/blog/replacing-cloudflare-workers-with-w7s/</guid>
            <pubDate>Sun, 31 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[W7S does not replace Cloudflare Workers as infrastructure. It replaces the repeated product workflow around Workers projects, bindings, deploys, URLs, environments, and app conventions.]]></description>
            <content:encoded><![CDATA[<p>Cloudflare Workers are powerful infrastructure. Workers, Pages, D1, KV, R2, Queues, Durable Objects, Workflows, AI, and service bindings give developers a large set of edge-native primitives.</p>
<p>That power is also the reason teams end up rebuilding the same product workflow:</p>
<ul>
<li class="">how should repositories deploy?</li>
<li class="">where do bindings live?</li>
<li class="">how should branch environments be named?</li>
<li class="">how should URLs be derived?</li>
<li class="">how should one app call another?</li>
<li class="">how are storage resources named and reused?</li>
<li class="">which settings live in the dashboard, Wrangler config, Terraform, or CI?</li>
</ul>
<p>W7S is built on a focused subset of these primitives. It does not replace Cloudflare Workers as the underlying runtime. It replaces the repeated app-platform layer many teams build on top.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-short-mapping">The Short Mapping<a href="https://w7s.io/docs/blog/replacing-cloudflare-workers-with-w7s/#the-short-mapping" class="hash-link" aria-label="Direct link to The Short Mapping" title="Direct link to The Short Mapping" translate="no">​</a></h2>
<table><thead><tr><th>Raw Cloudflare setup</th><th>W7S replacement</th><th>Best fit</th></tr></thead><tbody><tr><td>Wrangler deploy</td><td>W7S GitHub Action</td><td>Repo-native CI deploys</td></tr><tr><td>Worker script name</td><td>GitHub owner/repo identity</td><td>Predictable app identity</td></tr><tr><td>Workers routes</td><td>W7S owner/repo URL and custom domains</td><td>Convention-based routing</td></tr><tr><td>Wrangler bindings</td><td><code>w7s.json</code> bindings</td><td>App-declared runtime contract</td></tr><tr><td>Manual resource naming</td><td>Repo/environment-scoped resources</td><td>Reusable conventions</td></tr><tr><td>Service bindings</td><td>Backend RPC</td><td>Internal app-to-app calls</td></tr><tr><td>Queues setup</td><td>W7S queues</td><td>Background jobs</td></tr><tr><td>Cron triggers</td><td>W7S schedules</td><td>Time-based backend routes</td></tr><tr><td>Durable Objects config</td><td>Stateful Objects</td><td>Per-key durable state</td></tr><tr><td>Dashboard state</td><td>GitHub workflow plus manifest</td><td>Reviewable deploy surface</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="source-backed-comparison-points">Source-Backed Comparison Points<a href="https://w7s.io/docs/blog/replacing-cloudflare-workers-with-w7s/#source-backed-comparison-points" class="hash-link" aria-label="Direct link to Source-Backed Comparison Points" title="Direct link to Source-Backed Comparison Points" translate="no">​</a></h2>
<p>Cloudflare's own docs describe <a href="https://developers.cloudflare.com/workers/" target="_blank" rel="noopener noreferrer" class="">Workers</a> as a serverless execution environment and <a href="https://developers.cloudflare.com/pages/" target="_blank" rel="noopener noreferrer" class="">Pages</a> as a full-stack deployment product. That is a strong foundation, but it still leaves each team deciding how repositories, deploy credentials, preview branches, routes, and runtime configuration should be standardized across many apps.</p>
<p>The same Cloudflare documentation set also exposes the breadth of the primitive layer: <a href="https://developers.cloudflare.com/kv/" target="_blank" rel="noopener noreferrer" class="">KV</a> for key-value data, <a href="https://developers.cloudflare.com/r2/" target="_blank" rel="noopener noreferrer" class="">R2</a> for object storage, <a href="https://developers.cloudflare.com/d1/" target="_blank" rel="noopener noreferrer" class="">D1</a> for serverless SQL, <a href="https://developers.cloudflare.com/queues/" target="_blank" rel="noopener noreferrer" class="">Queues</a> for async delivery, and <a href="https://developers.cloudflare.com/workflows/" target="_blank" rel="noopener noreferrer" class="">Workflows</a> for durable multi-step work. W7S is compelling precisely because it narrows that surface into a repository contract instead of asking every small app to assemble the same menu again.</p>
<p>In W7S, <a class="" href="https://w7s.io/docs/deploy-from-github/">deploying from GitHub</a> is the primary release path. The CI workflow builds the app, the W7S action sends the archive, and the platform derives identity from the GitHub owner, repository, branch, and commit. That makes the deploy mechanism reviewable in code, which is harder to preserve when the important project state is split between Wrangler config, dashboard settings, manually named resources, and external automation.</p>
<p>The runtime shape is also documented in W7S instead of being left to convention. <a class="" href="https://w7s.io/docs/project-layouts/">Project layouts</a> explain how static output and native backend entrypoints are discovered, while <a class="" href="https://w7s.io/docs/storage-bindings/">storage bindings</a> define how databases, key-value stores, and file buckets are declared. This is where W7S becomes more than a thin deploy wrapper: it gives small apps a standard way to ask for platform resources.</p>
<p>The practical conclusion is not that teams should avoid Cloudflare. W7S depends on the same class of edge primitives and can also be <a class="" href="https://w7s.io/docs/self-host/">self-hosted</a> when a team wants its own domain and Cloudflare account boundary. The difference is product shape: raw Cloudflare gives maximum primitive control, while W7S gives a repeatable app-platform workflow for repositories that should not need bespoke infrastructure design.</p>
<p>The goal is not less Cloudflare. The goal is less repeated platform glue.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="w7s-is-a-product-workflow-on-top-of-primitives">W7S Is a Product Workflow on Top of Primitives<a href="https://w7s.io/docs/blog/replacing-cloudflare-workers-with-w7s/#w7s-is-a-product-workflow-on-top-of-primitives" class="hash-link" aria-label="Direct link to W7S Is a Product Workflow on Top of Primitives" title="Direct link to W7S Is a Product Workflow on Top of Primitives" translate="no">​</a></h2>
<p>With raw Workers, you decide how to wire the project:</p>
<div class="language-toml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">wrangler.toml</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-toml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">name = "api"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">main = "src/index.ts"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">compatibility_date = "2026-05-31"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">[[kv_namespaces]]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">binding = "CACHE"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">id = "..."</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">[[d1_databases]]</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">binding = "DB"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">database_name = "api-db"</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">database_id = "..."</span><br></div></code></pre></div></div>
<p>That is appropriate when the team wants direct Cloudflare control.</p>
<p>W7S chooses conventions for you:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"bindings"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"kv"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"CACHE"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"db"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"binding"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"DB"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"migrations"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"migrations"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The app declares intent. W7S maps that intent to platform resources using repository and environment identity.</p>
<p>This is the product stance: an app should not need to invent its own naming, routing, deployment, and authorization system before it can store data and serve requests.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="deploys-move-from-local-cli-to-github-actions">Deploys Move From Local CLI to GitHub Actions<a href="https://w7s.io/docs/blog/replacing-cloudflare-workers-with-w7s/#deploys-move-from-local-cli-to-github-actions" class="hash-link" aria-label="Direct link to Deploys Move From Local CLI to GitHub Actions" title="Direct link to Deploys Move From Local CLI to GitHub Actions" translate="no">​</a></h2>
<p>Raw Workers often start with local CLI deploys:</p>
<div class="language-sh codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sh codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">npx wrangler deploy</span><br></div></code></pre></div></div>
<p>That is good for direct control. W7S makes GitHub Actions the default release boundary:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">.github/workflows/deploy.yml</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> actions/checkout@v5</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> actions/setup</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">node@v6</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">node-version</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">22</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> npm ci</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> npm run build</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> w7s</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">io/w7s</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">cloud@v1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">token</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> github.token </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The deploy token is the GitHub token. The archive comes from CI. The platform verifies repository identity and publishes the app.</p>
<p>That makes deploys reviewable, repeatable, and easy to copy across repositories.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="worker-names-become-repository-names">Worker Names Become Repository Names<a href="https://w7s.io/docs/blog/replacing-cloudflare-workers-with-w7s/#worker-names-become-repository-names" class="hash-link" aria-label="Direct link to Worker Names Become Repository Names" title="Direct link to Worker Names Become Repository Names" translate="no">​</a></h2>
<p>Raw Workers need names, routes, domains, and environment conventions. Different teams choose different patterns.</p>
<p>W7S derives the default app identity from GitHub:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">github.com/acme/docs -&gt; https://acme.w7s.cloud/docs/</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">github.com/acme/api  -&gt; https://acme.w7s.cloud/api/</span><br></div></code></pre></div></div>
<p>Branch environments get branch-prefixed hosts. Custom domains can be declared in a <code>CNAME</code> file and authorized through DNS.</p>
<p>That convention is not as flexible as direct Cloudflare routing, but it removes decisions most small apps do not need to make.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="service-bindings-become-backend-rpc">Service Bindings Become Backend RPC<a href="https://w7s.io/docs/blog/replacing-cloudflare-workers-with-w7s/#service-bindings-become-backend-rpc" class="hash-link" aria-label="Direct link to Service Bindings Become Backend RPC" title="Direct link to Service Bindings Become Backend RPC" translate="no">​</a></h2>
<p>Cloudflare service bindings are a strong primitive for Worker-to-Worker calls. The raw setup still leaves teams to decide naming, authorization, and environment routing.</p>
<p>W7S wraps the common app-level case as Backend RPC:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/index.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> response </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">W7S_RPC</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token string" style="color:rgb(255, 121, 198)">"https://w7s.internal/api/v1/rpc/acme/auth/session"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    headers</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      authorization</span><span class="token operator">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token template-string string" style="color:rgb(255, 121, 198)">Bearer </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">${</span><span class="token template-string interpolation">env</span><span class="token template-string interpolation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token template-string interpolation constant" style="color:rgb(189, 147, 249)">W7S_RPC_TOKEN</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      cookie</span><span class="token operator">:</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">headers</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"cookie"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">??</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>W7S resolves the caller from the deployment token, checks target authorization, dispatches to the matching environment, and injects caller identity headers.</p>
<p>Use raw service bindings when you need Cloudflare-specific binding control. Use W7S RPC when repositories are the service boundary.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="bindings-stay-in-one-manifest">Bindings Stay in One Manifest<a href="https://w7s.io/docs/blog/replacing-cloudflare-workers-with-w7s/#bindings-stay-in-one-manifest" class="hash-link" aria-label="Direct link to Bindings Stay in One Manifest" title="Direct link to Bindings Stay in One Manifest" translate="no">​</a></h2>
<p>Raw Workers can use many Cloudflare resources, but each one has its own provisioning and configuration details.</p>
<p>W7S narrows the app-facing contract:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"bindings"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"db"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"binding"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"DB"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"migrations"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"migrations"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"kv"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"CACHE"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"fs"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"FILES"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"durableObjects"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"binding"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"COUNTER"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"className"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Counter"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"queues"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"jobs"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"workflows"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"process-order"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>This lets a reviewer understand the runtime surface from one file:</p>
<ul>
<li class="">data;</li>
<li class="">files;</li>
<li class="">stateful objects;</li>
<li class="">background jobs;</li>
<li class="">durable workflows;</li>
<li class="">runtime values.</li>
</ul>
<p>The platform can still use Cloudflare under the hood. The app does not have to expose every underlying configuration choice.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="branch-environments-become-a-first-class-convention">Branch Environments Become a First-Class Convention<a href="https://w7s.io/docs/blog/replacing-cloudflare-workers-with-w7s/#branch-environments-become-a-first-class-convention" class="hash-link" aria-label="Direct link to Branch Environments Become a First-Class Convention" title="Direct link to Branch Environments Become a First-Class Convention" translate="no">​</a></h2>
<p>Cloudflare can support multiple environments, preview deployments, Workers Builds, and many configuration shapes. The flexibility is useful, but teams often need conventions around what "staging" or "preview" means.</p>
<p>W7S chooses:</p>
<ul>
<li class="">production comes from <code>main</code> or <code>master</code>;</li>
<li class="">branches become environment names;</li>
<li class="">environment identity is injected into the backend;</li>
<li class="">resources are scoped by owner, repo, environment, type, and binding name.</li>
</ul>
<p>That makes a branch deploy feel like a real app, not just a script with a different name.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-raw-cloudflare-still-does-better">What Raw Cloudflare Still Does Better<a href="https://w7s.io/docs/blog/replacing-cloudflare-workers-with-w7s/#what-raw-cloudflare-still-does-better" class="hash-link" aria-label="Direct link to What Raw Cloudflare Still Does Better" title="Direct link to What Raw Cloudflare Still Does Better" translate="no">​</a></h2>
<p>Use raw Cloudflare Workers, Pages, Wrangler, Terraform, or Pulumi when you need:</p>
<ul>
<li class="">full control over Cloudflare account resources;</li>
<li class="">advanced routing and zone configuration;</li>
<li class="">compatibility flags and low-level runtime settings not surfaced by W7S;</li>
<li class="">non-W7S deployment flows;</li>
<li class="">custom Workers for Platforms behavior;</li>
<li class="">exact Cloudflare product feature access on day one;</li>
<li class="">direct operations through Cloudflare's dashboard, API, or IaC tools;</li>
<li class="">architectures that are not repository-app shaped.</li>
</ul>
<p>W7S intentionally gives up breadth to provide a smaller product workflow.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="recommendation">Recommendation<a href="https://w7s.io/docs/blog/replacing-cloudflare-workers-with-w7s/#recommendation" class="hash-link" aria-label="Direct link to Recommendation" title="Direct link to Recommendation" translate="no">​</a></h2>
<p>Use W7S when the repeated questions are more expensive than the platform choices:</p>
<ul>
<li class="">where do deploys live?</li>
<li class="">how are app URLs named?</li>
<li class="">how are bindings declared?</li>
<li class="">how do branch environments work?</li>
<li class="">how does one backend call another?</li>
<li class="">how are queues and workflows wired?</li>
</ul>
<p>Use raw Cloudflare when the application needs direct infrastructure control.</p>
<p>W7S is not an alternative to Cloudflare as infrastructure. It is an alternative to every team rebuilding a small app platform from Cloudflare primitives.</p>]]></content:encoded>
            <category>platforms</category>
            <category>cloudflare</category>
            <category>workers</category>
            <category>bindings</category>
        </item>
        <item>
            <title><![CDATA[Replacing Dapr With W7S Components]]></title>
            <link>https://w7s.io/docs/blog/replacing-dapr-with-w7s-components/</link>
            <guid>https://w7s.io/docs/blog/replacing-dapr-with-w7s-components/</guid>
            <pubDate>Sun, 31 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[How to map Dapr sidecars, service invocation, pub/sub, state stores, bindings, actors, workflows, secrets, config, and observability onto W7S-native primitives.]]></description>
            <content:encoded><![CDATA[<p>Dapr is a strong distributed application runtime. It gives teams a sidecar, service invocation, pub/sub, state management, bindings, actors, workflows, secrets, configuration, and a component model that can sit beside services across different hosting environments.</p>
<p>W7S starts from a different premise. If the application already deploys through W7S, the platform can own many of those concerns directly: deployment identity, internal routing, managed bindings, storage provisioning, background delivery, workflow dispatch, local multi-repo testing, and usage accounting.</p>
<p>That makes the useful question narrower than "can W7S run Dapr?"</p>
<blockquote>
<p>Can the product behavior people reach for Dapr to get be built from W7S components that already exist?</p>
</blockquote>
<p>For many W7S apps, yes.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-short-mapping">The Short Mapping<a href="https://w7s.io/docs/blog/replacing-dapr-with-w7s-components/#the-short-mapping" class="hash-link" aria-label="Direct link to The Short Mapping" title="Direct link to The Short Mapping" translate="no">​</a></h2>
<table><thead><tr><th>Dapr concept</th><th>W7S replacement</th><th>Best fit</th></tr></thead><tbody><tr><td>Sidecar runtime</td><td>W7S platform bindings</td><td>Platform-owned routing, identity, and resource access</td></tr><tr><td>App ID and name resolution</td><td>GitHub owner/repo identity</td><td>Repo-scoped services and environments</td></tr><tr><td>Service invocation</td><td>Backend RPC</td><td>Synchronous service-to-service calls</td></tr><tr><td>Pub/sub</td><td>Event router plus queues</td><td>Explicit fanout to known subscribers</td></tr><tr><td>State management</td><td>Serverless DB, KV, FS, or stateful objects</td><td>Durable app state scoped to repo and environment</td></tr><tr><td>Bindings</td><td>W7S managed bindings and backend routes</td><td>Storage, queues, schedules, files, AI, and external service access</td></tr><tr><td>Actors</td><td>Stateful Objects</td><td>Per-key durable compute and state</td></tr><tr><td>Workflows</td><td>W7S Workflows</td><td>Retryable business processes</td></tr><tr><td>Secrets</td><td>GitHub secrets plus W7S secret bindings</td><td>Deployment-time secret injection</td></tr><tr><td>Configuration</td><td><code>w7s.json</code>, vars, KV, DB</td><td>Versioned runtime contract and app-owned config</td></tr><tr><td>Resiliency policies</td><td>Queues, workflows, and explicit retry helpers</td><td>Retry where the product needs it</td></tr><tr><td>Local runtime</td><td><code>w7s-local</code> plus service doubles</td><td>Testing repo boundaries without a sidecar mesh</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="source-backed-comparison-points">Source-Backed Comparison Points<a href="https://w7s.io/docs/blog/replacing-dapr-with-w7s-components/#source-backed-comparison-points" class="hash-link" aria-label="Direct link to Source-Backed Comparison Points" title="Direct link to Source-Backed Comparison Points" translate="no">​</a></h2>
<p>Dapr's current docs describe it as a runtime built around a <a href="https://docs.dapr.io/concepts/overview/" target="_blank" rel="noopener noreferrer" class="">sidecar architecture and distributed application building blocks</a>. That is the right abstraction when a team needs one API surface across different languages, hosts, and infrastructure providers. It is also a meaningful operational choice: every app now depends on the sidecar lifecycle, component configuration, ports, and runtime behavior.</p>
<p>The most common Dapr building blocks map to product behaviors W7S already tries to provide inside the platform. Dapr <a href="https://docs.dapr.io/developing-applications/building-blocks/service-invocation/" target="_blank" rel="noopener noreferrer" class="">service invocation</a> becomes <a class="" href="https://w7s.io/docs/backend-rpc/">Backend RPC</a> when W7S apps call each other by repository identity. Dapr <a href="https://docs.dapr.io/developing-applications/building-blocks/pubsub/" target="_blank" rel="noopener noreferrer" class="">publish and subscribe</a> becomes explicit event fanout over <a class="" href="https://w7s.io/docs/backend-queues/">backend queues</a> when the subscribers are known W7S apps.</p>
<p>State is another place where W7S narrows the problem. Dapr has a broad <a href="https://docs.dapr.io/developing-applications/building-blocks/state-management/" target="_blank" rel="noopener noreferrer" class="">state management</a> abstraction because it must support many backing stores. W7S instead lets a repository declare app-local data through <a class="" href="https://w7s.io/docs/storage-bindings/">storage bindings</a>, <a class="" href="https://w7s.io/docs/serverless-database/">serverless database</a>, key-value storage, or file storage, with environment-specific provisioning handled by the platform.</p>
<p>Dapr <a href="https://docs.dapr.io/developing-applications/building-blocks/actors/" target="_blank" rel="noopener noreferrer" class="">actors</a> and <a href="https://docs.dapr.io/developing-applications/building-blocks/workflow/" target="_blank" rel="noopener noreferrer" class="">workflow</a> are powerful when the application needs virtual actor semantics or durable orchestration across services. W7S should not pretend to be a drop-in implementation of those APIs. The W7S-native path is <a class="" href="https://w7s.io/docs/backend-durable-objects/">stateful objects</a> for per-key state and <a class="" href="https://w7s.io/docs/backend-workflows/">backend workflows</a> for retryable business processes that fit the platform runtime.</p>
<p>That framing keeps the article honest. Dapr remains the better fit for heterogeneous fleets that require its exact API, sidecar model, or portability promise. W7S is the better fit when the app already lives in W7S and the team wants fewer moving parts: repository identity, internal RPC, queues, storage, workflows, local testing with <code>w7s-local</code>, and usage accounting from one deployment surface.</p>
<p>This is not a claim that W7S is a drop-in Dapr runtime. Dapr is built to be portable across hosts, languages, and infrastructure providers. W7S is built to make a GitHub repository deploy as an app with first-class platform bindings.</p>
<p>If the app needs Dapr's exact API surface, Dapr can remain an external dependency. If the app is using Dapr mostly to avoid rebuilding common distributed app plumbing, W7S can usually provide a smaller default.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="sidecars-become-platform-bindings">Sidecars Become Platform Bindings<a href="https://w7s.io/docs/blog/replacing-dapr-with-w7s-components/#sidecars-become-platform-bindings" class="hash-link" aria-label="Direct link to Sidecars Become Platform Bindings" title="Direct link to Sidecars Become Platform Bindings" translate="no">​</a></h2>
<p>Dapr puts a runtime process next to each service. The application talks to that sidecar over HTTP or gRPC, and the sidecar handles service discovery, component calls, state stores, pub/sub brokers, secret stores, and other building blocks.</p>
<p>That is valuable when the hosting platform is not opinionated enough to provide those pieces consistently. It also has costs:</p>
<ul>
<li class="">every service needs a sidecar process;</li>
<li class="">local development needs Dapr initialization and component config;</li>
<li class="">production needs sidecar lifecycle, ports, health checks, and resource limits;</li>
<li class="">the app now depends on another runtime contract beside the host.</li>
</ul>
<p>In W7S, the deployment platform already knows the repository, owner, branch environment, runtime, and declared resources. For JavaScript/TypeScript native backends, W7S injects bindings directly into <code>env</code>:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">W7S_RPC</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">W7S_QUEUE</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">W7S_WORKFLOW</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">W7S_AI</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">W7S_OWNER</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">W7S_REPO</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">W7S_REPOSITORY</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">W7S_ENVIRONMENT</span><br></div></code></pre></div></div>
<p>App-declared storage bindings such as DB, KV, and FS are also exposed on <code>env</code>. The backend calls the binding directly rather than sending every operation through a local sidecar.</p>
<p>That is the core tradeoff. Dapr standardizes through a sidecar API. W7S standardizes through repository deployment metadata and platform bindings.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="app-identity-becomes-repository-identity">App Identity Becomes Repository Identity<a href="https://w7s.io/docs/blog/replacing-dapr-with-w7s-components/#app-identity-becomes-repository-identity" class="hash-link" aria-label="Direct link to App Identity Becomes Repository Identity" title="Direct link to App Identity Becomes Repository Identity" translate="no">​</a></h2>
<p>Dapr service invocation depends on app IDs. W7S already has a stable app identity: the GitHub owner and repository, plus the deployment environment.</p>
<p>For example:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">github.com/acme/auth</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">github.com/acme/checkout</span><br></div></code></pre></div></div>
<p>become W7S repositories:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">acme/auth</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">acme/checkout</span><br></div></code></pre></div></div>
<p>Production deploys call production targets. Branch deploys call matching branch environments when those targets exist. Same-owner calls are allowed by default, while cross-owner calls stay target-controlled through allowlists.</p>
<p>This removes a separate service naming layer. The repo is the service boundary.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="service-invocation-becomes-backend-rpc">Service Invocation Becomes Backend RPC<a href="https://w7s.io/docs/blog/replacing-dapr-with-w7s-components/#service-invocation-becomes-backend-rpc" class="hash-link" aria-label="Direct link to Service Invocation Becomes Backend RPC" title="Direct link to Service Invocation Becomes Backend RPC" translate="no">​</a></h2>
<p>Dapr service invocation often looks like this:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/auth-client.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> response </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token string" style="color:rgb(255, 121, 198)">"http://localhost:3500/v1.0/invoke/auth/method/session"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    headers</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      cookie</span><span class="token operator">:</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">headers</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"cookie"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">??</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>The application calls the local sidecar, and the sidecar routes to the service named <code>auth</code>.</p>
<p>In W7S, a backend calls the target repository through <code>W7S_RPC</code>:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/auth-client.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">type</span><span class="token plain"> </span><span class="token class-name">Env</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token constant" style="color:rgb(189, 147, 249)">W7S_RPC</span><span class="token operator">:</span><span class="token plain"> Fetcher</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token constant" style="color:rgb(189, 147, 249)">W7S_RPC_TOKEN</span><span class="token operator">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">string</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">getSession</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token operator">:</span><span class="token plain"> Request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> env</span><span class="token operator">:</span><span class="token plain"> Env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> response </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">W7S_RPC</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token string" style="color:rgb(255, 121, 198)">"https://w7s.internal/api/v1/rpc/acme/auth/session"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      headers</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        authorization</span><span class="token operator">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token template-string string" style="color:rgb(255, 121, 198)">Bearer </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">${</span><span class="token template-string interpolation">env</span><span class="token template-string interpolation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token template-string interpolation constant" style="color:rgb(189, 147, 249)">W7S_RPC_TOKEN</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        cookie</span><span class="token operator">:</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">headers</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"cookie"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">??</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token operator">!</span><span class="token plain">response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">ok</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">throw</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">Error</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Auth service unavailable"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The target backend receives a normal HTTP request at <code>/session</code>. W7S resolves the caller from the deployment token, checks authorization, dispatches to the target in the same environment, and injects caller identity headers.</p>
<p>Use RPC when the caller needs a response before it can finish its own request.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="pubsub-becomes-explicit-event-fanout">Pub/Sub Becomes Explicit Event Fanout<a href="https://w7s.io/docs/blog/replacing-dapr-with-w7s-components/#pubsub-becomes-explicit-event-fanout" class="hash-link" aria-label="Direct link to Pub/Sub Becomes Explicit Event Fanout" title="Direct link to Pub/Sub Becomes Explicit Event Fanout" translate="no">​</a></h2>
<p>Dapr pub/sub gives an app a stable publish API while the underlying broker can be Redis, Kafka, Azure Service Bus, NATS, RabbitMQ, or another component.</p>
<p>That abstraction is useful when broker portability is the primary goal. In W7S, the better default is often explicit app-level fanout:</p>
<ol>
<li class="">a publisher calls an event-router backend through RPC;</li>
<li class="">the router validates the subject and publisher;</li>
<li class="">the router looks up subscribers from repo metadata or app config;</li>
<li class="">the router enqueues one queue message per subscriber;</li>
<li class="">each subscriber consumes its own W7S queue.</li>
</ol>
<p>The queue consumer declares its queue in <code>w7s.json</code>:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"queues"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"events"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"consumer"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/_w7s/queues/events"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The router sends to the subscriber through <code>W7S_QUEUE</code>:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/event-router.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">type</span><span class="token plain"> </span><span class="token class-name">Env</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token constant" style="color:rgb(189, 147, 249)">W7S_QUEUE</span><span class="token operator">:</span><span class="token plain"> Fetcher</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token constant" style="color:rgb(189, 147, 249)">W7S_QUEUE_TOKEN</span><span class="token operator">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">string</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">deliver</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">env</span><span class="token operator">:</span><span class="token plain"> Env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> event</span><span class="token operator">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">unknown</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">W7S_QUEUE</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token string" style="color:rgb(255, 121, 198)">"https://w7s.internal/api/v1/queues/acme/email-worker/events"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      method</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"POST"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      headers</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        authorization</span><span class="token operator">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token template-string string" style="color:rgb(255, 121, 198)">Bearer </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">${</span><span class="token template-string interpolation">env</span><span class="token template-string interpolation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token template-string interpolation constant" style="color:rgb(189, 147, 249)">W7S_QUEUE_TOKEN</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token string-property property">"content-type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"application/json"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      body</span><span class="token operator">:</span><span class="token plain"> </span><span class="token constant" style="color:rgb(189, 147, 249)">JSON</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">stringify</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">event</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The consumer receives a queue batch:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/index.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">async</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token operator">:</span><span class="token plain"> Request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> url </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name constant" style="color:rgb(189, 147, 249)">URL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">pathname </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/_w7s/queues/events"</span><span class="token plain"> </span><span class="token operator">&amp;&amp;</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">method </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"POST"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> batch </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">for</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> message </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">of</span><span class="token plain"> batch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">messages</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token builtin" style="color:rgb(189, 147, 249)">console</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">log</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"event"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> message</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">body</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">ok</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> processed</span><span class="token operator">:</span><span class="token plain"> batch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">messages</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">length</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Not found"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">status</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">404</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>This is less dynamic than Dapr broker-backed topics, but it keeps topology visible in repositories and W7S metadata. For many product systems, that is an advantage.</p>
<p>The runnable local version of this pattern is in
<a href="https://github.com/w7s-io/docs/tree/main/examples/w7s-local-native-events" target="_blank" rel="noopener noreferrer" class=""><code>examples/w7s-local-native-events</code></a>.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="state-stores-become-app-owned-storage">State Stores Become App-Owned Storage<a href="https://w7s.io/docs/blog/replacing-dapr-with-w7s-components/#state-stores-become-app-owned-storage" class="hash-link" aria-label="Direct link to State Stores Become App-Owned Storage" title="Direct link to State Stores Become App-Owned Storage" translate="no">​</a></h2>
<p>Dapr state management gives apps a key/value API over a configured state store. That is a useful portable floor, especially when many services need the same API across hosts.</p>
<p>W7S has a different storage model: declare the storage the app actually needs, then read the binding directly from the backend.</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"bindings"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"kv"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"CACHE"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"fs"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"FILES"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"db"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"binding"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"DB"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"migrations"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"migrations"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Use the narrowest storage primitive that matches the data:</p>
<table><thead><tr><th>State need</th><th>W7S component</th></tr></thead><tbody><tr><td>App-local relational data</td><td>Serverless DB</td></tr><tr><td>Cache or latest value by key</td><td>KV</td></tr><tr><td>Files and larger payloads</td><td>FS</td></tr><tr><td>Per-entity state with compute</td><td>Stateful Objects</td></tr><tr><td>External Postgres</td><td>Managed Postgres binding</td></tr></tbody></table>
<p>For relational app state, keep migrations in the repo:</p>
<div class="language-sql codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">migrations/0001_create_orders.sql</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sql codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">CREATE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TABLE</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">IF</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">EXISTS</span><span class="token plain"> orders </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  id </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">PRIMARY</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">KEY</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  email </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  amount </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">INTEGER</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  created_at </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">TEXT</span><span class="token plain"> </span><span class="token operator">NOT</span><span class="token plain"> </span><span class="token boolean">NULL</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>Then query through <code>env.DB</code>:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/index.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">type</span><span class="token plain"> </span><span class="token class-name">Env</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token constant" style="color:rgb(189, 147, 249)">DB</span><span class="token operator">:</span><span class="token plain"> D1Database</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">saveOrder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">env</span><span class="token operator">:</span><span class="token plain"> Env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> order</span><span class="token operator">:</span><span class="token plain"> Order</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">DB</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">prepare</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token string" style="color:rgb(255, 121, 198)">"insert into orders (id, email, amount, created_at) values (?, ?, ?, ?)"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">bind</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">order</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> order</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">email</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> order</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">amount</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">Date</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">toISOString</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">run</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>This gives up Dapr's common state API, but it gives the app a clearer data model and a deployment-owned migration path.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="bindings-become-specific-w7s-bindings">Bindings Become Specific W7S Bindings<a href="https://w7s.io/docs/blog/replacing-dapr-with-w7s-components/#bindings-become-specific-w7s-bindings" class="hash-link" aria-label="Direct link to Bindings Become Specific W7S Bindings" title="Direct link to Bindings Become Specific W7S Bindings" translate="no">​</a></h2>
<p>Dapr bindings provide a common way to call external systems and receive events from them. W7S should not reproduce that as a single generic binding layer unless the platform genuinely needs it.</p>
<p>The W7S default is more direct:</p>
<ul>
<li class="">queues for background work;</li>
<li class="">schedules for cron-style triggers;</li>
<li class="">workflows for durable processes;</li>
<li class="">FS for files;</li>
<li class="">DB and KV for data;</li>
<li class="">AI bindings for model calls;</li>
<li class="">runtime vars and secrets for provider credentials;</li>
<li class="">normal <code>fetch</code> for external HTTP APIs.</li>
</ul>
<p>For a time-based trigger, declare a schedule:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"schedules"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"cron"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"*/5 * * * *"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"path"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/_w7s/schedules/sync"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Handle it as a backend route:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/index.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">async</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token operator">:</span><span class="token plain"> Request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> env</span><span class="token operator">:</span><span class="token plain"> Env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> url </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name constant" style="color:rgb(189, 147, 249)">URL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">pathname </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/_w7s/schedules/sync"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">syncExternalSystem</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">ok</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Not found"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">status</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">404</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>That keeps the trigger contract in the repository instead of hiding it behind a generic component file.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="actors-become-stateful-objects">Actors Become Stateful Objects<a href="https://w7s.io/docs/blog/replacing-dapr-with-w7s-components/#actors-become-stateful-objects" class="hash-link" aria-label="Direct link to Actors Become Stateful Objects" title="Direct link to Actors Become Stateful Objects" translate="no">​</a></h2>
<p>Dapr actors provide a virtual actor model: each actor has identity, state, single-threaded execution, activation, deactivation, timers, and reminders.</p>
<p>W7S Stateful Objects cover the most common app need behind that model: route all operations for one logical entity to one durable object with attached state.</p>
<p>Declare the object class:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"bindings"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"durableObjects"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"binding"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"CART"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"className"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Cart"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Implement the object:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/index.js</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword module" style="color:rgb(189, 147, 249);font-style:italic">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">Cart</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token function" style="color:rgb(80, 250, 123)">constructor</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token parameter">ctx</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">this</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token property-access">ctx</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> ctx</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">async</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token parameter">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> url </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">URL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token property-access">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> items </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword control-flow" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">this</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token property-access">ctx</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token property-access">storage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token method function property-access" style="color:rgb(80, 250, 123)">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"items"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">??</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword control-flow" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token property-access">pathname</span><span class="token plain"> </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/add"</span><span class="token plain"> </span><span class="token operator">&amp;&amp;</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token property-access">method</span><span class="token plain"> </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"POST"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> item </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token method function property-access" style="color:rgb(80, 250, 123)">json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> next </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token spread operator">...</span><span class="token plain">items</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> item</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword control-flow" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">this</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token property-access">ctx</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token property-access">storage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token method function property-access" style="color:rgb(80, 250, 123)">put</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"items"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> next</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword control-flow" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token maybe-class-name">Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token method function property-access" style="color:rgb(80, 250, 123)">json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token literal-property property">items</span><span class="token operator">:</span><span class="token plain"> next</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword control-flow" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token maybe-class-name">Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token method function property-access" style="color:rgb(80, 250, 123)">json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">items</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword module" style="color:rgb(189, 147, 249);font-style:italic">export</span><span class="token plain"> </span><span class="token keyword module" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token parameter">request</span><span class="token parameter punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token parameter"> env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> cartId </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">URL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token property-access">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token property-access">searchParams</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token method function property-access" style="color:rgb(80, 250, 123)">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"cart"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">??</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"default"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> id </span><span class="token operator">=</span><span class="token plain"> env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">CART</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token method function property-access" style="color:rgb(80, 250, 123)">idFromName</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">cartId</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword control-flow" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">CART</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token method function property-access" style="color:rgb(80, 250, 123)">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token method function property-access" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>This is not a full clone of Dapr actors. If the application depends on actor reminders, placement behavior, or Dapr actor SDK semantics, keep Dapr or build those semantics explicitly. If the application needs durable per-key compute and state, Stateful Objects are the W7S-native fit.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="workflows-stay-workflows">Workflows Stay Workflows<a href="https://w7s.io/docs/blog/replacing-dapr-with-w7s-components/#workflows-stay-workflows" class="hash-link" aria-label="Direct link to Workflows Stay Workflows" title="Direct link to Workflows Stay Workflows" translate="no">​</a></h2>
<p>Dapr workflows are for long-running, fault-tolerant, stateful processes. W7S Workflows target the same product need: start a named process, persist the instance, dispatch work to the app, retry failures, and expose status.</p>
<p>Declare a workflow:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"workflows"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"checkout"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"path"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/_w7s/workflows/checkout"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Start it through the platform binding:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/index.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">type</span><span class="token plain"> </span><span class="token class-name">Env</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token constant" style="color:rgb(189, 147, 249)">W7S_WORKFLOW</span><span class="token operator">:</span><span class="token plain"> Fetcher</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token constant" style="color:rgb(189, 147, 249)">W7S_WORKFLOW_TOKEN</span><span class="token operator">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">string</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">startCheckout</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">env</span><span class="token operator">:</span><span class="token plain"> Env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> orderId</span><span class="token operator">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">string</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">W7S_WORKFLOW</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token string" style="color:rgb(255, 121, 198)">"https://w7s.internal/api/v1/workflows/acme/checkout/checkout"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      method</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"POST"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      headers</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        authorization</span><span class="token operator">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token template-string string" style="color:rgb(255, 121, 198)">Bearer </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">${</span><span class="token template-string interpolation">env</span><span class="token template-string interpolation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token template-string interpolation constant" style="color:rgb(189, 147, 249)">W7S_WORKFLOW_TOKEN</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token string-property property">"content-type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"application/json"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token string-property property">"x-w7s-workflow-instance-id"</span><span class="token operator">:</span><span class="token plain"> orderId</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      body</span><span class="token operator">:</span><span class="token plain"> </span><span class="token constant" style="color:rgb(189, 147, 249)">JSON</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">stringify</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">orderId</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The app receives the workflow run at the configured path:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/index.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">pathname </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/_w7s/workflows/checkout"</span><span class="token plain"> </span><span class="token operator">&amp;&amp;</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">method </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"POST"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> run </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">processCheckout</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">run</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">payload</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">orderId</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">ok</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>W7S Workflows deliberately keep the app API small. The platform owns the workflow runner; the app owns the business step.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="secrets-and-configuration-stay-in-the-deploy-contract">Secrets and Configuration Stay in the Deploy Contract<a href="https://w7s.io/docs/blog/replacing-dapr-with-w7s-components/#secrets-and-configuration-stay-in-the-deploy-contract" class="hash-link" aria-label="Direct link to Secrets and Configuration Stay in the Deploy Contract" title="Direct link to Secrets and Configuration Stay in the Deploy Contract" translate="no">​</a></h2>
<p>Dapr can read secrets and configuration through component APIs. W7S keeps these concerns closer to GitHub and the deployment manifest.</p>
<p>Declare the values the backend expects:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"vars"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"PUBLIC_STRIPE_KEY"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"secrets"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"STRIPE_SECRET_KEY"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Pass them from GitHub Actions:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">.github/workflows/deploy.yml</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> w7s</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">io/w7s</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">cloud@v1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">PUBLIC_STRIPE_KEY</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> vars.PUBLIC_STRIPE_KEY </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">STRIPE_SECRET_KEY</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> secrets.STRIPE_SECRET_KEY </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">token</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> github.token </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Read them from <code>env</code> in the backend:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/index.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">type</span><span class="token plain"> </span><span class="token class-name">Env</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token constant" style="color:rgb(189, 147, 249)">PUBLIC_STRIPE_KEY</span><span class="token operator">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">string</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token constant" style="color:rgb(189, 147, 249)">STRIPE_SECRET_KEY</span><span class="token operator">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">string</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>For dynamic app configuration, use app-owned storage:</p>
<ul>
<li class="">KV for simple flags and values;</li>
<li class="">DB for configuration that needs audit, ownership, or query filters;</li>
<li class="">FS for larger config documents.</li>
</ul>
<p>That separates deploy-time secrets from runtime product configuration.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="resiliency-is-attached-to-the-primitive">Resiliency Is Attached to the Primitive<a href="https://w7s.io/docs/blog/replacing-dapr-with-w7s-components/#resiliency-is-attached-to-the-primitive" class="hash-link" aria-label="Direct link to Resiliency Is Attached to the Primitive" title="Direct link to Resiliency Is Attached to the Primitive" translate="no">​</a></h2>
<p>Dapr has a resiliency model that can apply retries, timeouts, and circuit breakers around building block calls. That is useful in a general distributed runtime.</p>
<p>In W7S, resiliency should usually be attached to the primitive that needs it:</p>
<ul>
<li class="">queues retry background delivery;</li>
<li class="">workflows retry durable process steps;</li>
<li class="">schedules retry by creating the next scheduled attempt;</li>
<li class="">RPC callers use explicit timeout and retry helpers where the product can tolerate repeat calls;</li>
<li class="">database writes use normal transaction and idempotency patterns.</li>
</ul>
<p>A simple RPC helper can make retry behavior visible:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/w7s-rpc.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">callWithTimeout</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  fetcher</span><span class="token operator">:</span><span class="token plain"> Fetcher</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  url</span><span class="token operator">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">string</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  init</span><span class="token operator">:</span><span class="token plain"> RequestInit</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  timeoutMs </span><span class="token operator">=</span><span class="token plain"> </span><span class="token number">3000</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> controller </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">AbortController</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> timer </span><span class="token operator">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">setTimeout</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">=&gt;</span><span class="token plain"> controller</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">abort</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> timeoutMs</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">try</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> fetcher</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token operator">...</span><span class="token plain">init</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      signal</span><span class="token operator">:</span><span class="token plain"> controller</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">signal</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">finally</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token function" style="color:rgb(80, 250, 123)">clearTimeout</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">timer</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Do not hide every failure behind a global policy. Some operations are safe to retry, some need idempotency keys, and some should fail fast.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="observability-becomes-repo-scoped-logs-and-usage">Observability Becomes Repo-Scoped Logs and Usage<a href="https://w7s.io/docs/blog/replacing-dapr-with-w7s-components/#observability-becomes-repo-scoped-logs-and-usage" class="hash-link" aria-label="Direct link to Observability Becomes Repo-Scoped Logs and Usage" title="Direct link to Observability Becomes Repo-Scoped Logs and Usage" translate="no">​</a></h2>
<p>Dapr gives a consistent runtime surface for metrics, tracing, and sidecar diagnostics. W7S can provide the platform part from deployment identity:</p>
<ul>
<li class="">logs are associated with repository and environment;</li>
<li class="">RPC, queue, workflow, schedule, deploy, and runtime usage can be counted per app;</li>
<li class="">the GitHub Action can report deploy summaries and usage warnings;</li>
<li class="">branch environments can be inspected separately from production.</li>
</ul>
<p>This is a different observability boundary. Dapr observes a distributed runtime. W7S observes repository deployments and platform-managed dispatch.</p>
<p>Apps can still emit structured logs and external telemetry where they need deeper product traces.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="local-development-without-dapr">Local Development Without Dapr<a href="https://w7s.io/docs/blog/replacing-dapr-with-w7s-components/#local-development-without-dapr" class="hash-link" aria-label="Direct link to Local Development Without Dapr" title="Direct link to Local Development Without Dapr" translate="no">​</a></h2>
<p>Dapr's local runtime is one of its strongest developer conveniences. W7S needs a local story too, but it should match the W7S deployment model.</p>
<p>Use <code>w7s-local</code> for repo-shaped routing:</p>
<div class="language-sh codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sh codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">w7s-local \</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  --owner acme \</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  --repo checkout \</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  --port 8790 \</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  --command "npm run dev" \</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  --backend http://127.0.0.1:5173</span><br></div></code></pre></div></div>
<p>For multi-repo tests, run one local W7S router per repo and put platform calls behind small helpers:</p>
<ul>
<li class="">hosted W7S calls <code>env.W7S_RPC</code>, <code>env.W7S_QUEUE</code>, or <code>env.W7S_WORKFLOW</code>;</li>
<li class="">local development calls the target repo's <code>w7s-local</code> URL or a service double.</li>
</ul>
<p>That keeps local tests honest about repository boundaries without requiring every developer to run a sidecar runtime and component stack.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-w7s-still-does-not-replace">What W7S Still Does Not Replace<a href="https://w7s.io/docs/blog/replacing-dapr-with-w7s-components/#what-w7s-still-does-not-replace" class="hash-link" aria-label="Direct link to What W7S Still Does Not Replace" title="Direct link to What W7S Still Does Not Replace" translate="no">​</a></h2>
<p>There are Dapr capabilities W7S should not pretend to replace today:</p>
<ul>
<li class="">a language-agnostic sidecar API for any host;</li>
<li class="">Dapr's exact HTTP and gRPC API contracts;</li>
<li class="">broker and state-store portability through Dapr component specs;</li>
<li class="">Dapr actor SDK semantics, placement, timers, and reminders;</li>
<li class="">Dapr's component ecosystem for many external services;</li>
<li class="">Kubernetes-native Dapr operator behavior;</li>
<li class="">Dapr resiliency policy resources;</li>
<li class="">Dapr mTLS and sidecar-to-sidecar service mesh behavior.</li>
</ul>
<p>If a team is standardizing across Kubernetes, VMs, edge nodes, local containers, and multiple languages, Dapr may still be the right abstraction. W7S compatibility should then mean making it easy to call external Dapr-enabled services, not pretending W7S is the Dapr runtime.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-w7s-native-distributed-app-layer">A W7S-Native Distributed App Layer<a href="https://w7s.io/docs/blog/replacing-dapr-with-w7s-components/#a-w7s-native-distributed-app-layer" class="hash-link" aria-label="Direct link to A W7S-Native Distributed App Layer" title="Direct link to A W7S-Native Distributed App Layer" translate="no">​</a></h2>
<p>The W7S direction should be smaller than reimplementing Dapr.</p>
<p>The most useful next layer is a W7S-native distributed app contract built from existing pieces:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"services"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"call"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"acme/auth"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"acme/billing"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"events"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"publish"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"orders.created"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"subscribe"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"subject"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"orders.created"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"queue"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"events"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"queues"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"events"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"workflows"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"checkout"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"bindings"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"db"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> </span><span class="token property">"binding"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"DB"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token property">"migrations"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"migrations"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"kv"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"CACHE"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Under the hood, W7S can reuse deployment metadata, managed bindings, queue delivery, workflow dispatch, per-repo authorization, branch environment isolation, and usage accounting.</p>
<p>That gives W7S much of the ergonomics people want from Dapr without adding a sidecar runtime as a required platform dependency.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="recommendation">Recommendation<a href="https://w7s.io/docs/blog/replacing-dapr-with-w7s-components/#recommendation" class="hash-link" aria-label="Direct link to Recommendation" title="Direct link to Recommendation" translate="no">​</a></h2>
<p>Do not add Dapr as a required W7S dependency.</p>
<p>Use W7S-native primitives first:</p>
<ul>
<li class="">Backend RPC for synchronous service invocation;</li>
<li class="">queues and an event router for async fanout;</li>
<li class="">Serverless DB, KV, FS, and Stateful Objects for state;</li>
<li class="">schedules for time-based triggers;</li>
<li class="">workflows for durable business processes;</li>
<li class=""><code>w7s.json</code>, vars, and secrets for the runtime contract;</li>
<li class=""><code>w7s-local</code> for multi-repo development.</li>
</ul>
<p>Keep Dapr for applications that truly need Dapr's sidecar API, component ecosystem, actor runtime, or cross-host portability. For W7S-native apps, the simpler path is to let W7S be the platform instead of adding another distributed runtime beside it.</p>]]></content:encoded>
            <category>architecture</category>
            <category>rpc</category>
            <category>queues</category>
            <category>workflows</category>
            <category>stateful-objects</category>
        </item>
        <item>
            <title><![CDATA[Replacing GitHub Pages With W7S]]></title>
            <link>https://w7s.io/docs/blog/replacing-github-pages-with-w7s/</link>
            <guid>https://w7s.io/docs/blog/replacing-github-pages-with-w7s/</guid>
            <pubDate>Sun, 31 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[GitHub Pages is excellent for static sites. W7S is the next step when the same repository needs backend routes, storage, queues, schedules, workflows, or custom runtime bindings.]]></description>
            <content:encoded><![CDATA[<p>GitHub Pages is one of the cleanest ways to publish a static site. Put HTML, CSS, and JavaScript in a repository, optionally run a build, and publish the result.</p>
<p>For many docs sites, personal sites, and project pages, that is exactly enough.</p>
<p>W7S is for the moment after "static is enough" stops being true:</p>
<ul>
<li class="">the docs site needs a search endpoint;</li>
<li class="">the landing page needs a form handler;</li>
<li class="">the project page needs a status API;</li>
<li class="">the app needs a database, file bucket, queue, schedule, or backend route;</li>
<li class="">branch previews need isolated runtime resources.</li>
</ul>
<blockquote>
<p>GitHub Pages is static hosting. W7S is repository-native app hosting.</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-short-mapping">The Short Mapping<a href="https://w7s.io/docs/blog/replacing-github-pages-with-w7s/#the-short-mapping" class="hash-link" aria-label="Direct link to The Short Mapping" title="Direct link to The Short Mapping" translate="no">​</a></h2>
<table><thead><tr><th>GitHub Pages concept</th><th>W7S replacement</th><th>Best fit</th></tr></thead><tbody><tr><td>Static site</td><td>Static asset deploy</td><td>HTML, CSS, JS, docs, landing pages</td></tr><tr><td>Jekyll or Actions build</td><td>GitHub Actions build</td><td>Any build toolchain</td></tr><tr><td><code>github.io</code> URL</td><td>W7S owner/repo URL</td><td>Predictable app URL</td></tr><tr><td>Custom domain</td><td><code>CNAME</code> plus DNS authorization</td><td>Repo-visible hostname claims</td></tr><tr><td>Branch or docs folder source</td><td>Workflow-selected output directory</td><td>Explicit packaging</td></tr><tr><td>No backend</td><td>Native backend</td><td>API routes beside static assets</td></tr><tr><td>No storage</td><td>DB, KV, FS bindings</td><td>App data without another platform</td></tr><tr><td>No background jobs</td><td>Queues, schedules, workflows</td><td>Async and timed work</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="source-backed-comparison-points">Source-Backed Comparison Points<a href="https://w7s.io/docs/blog/replacing-github-pages-with-w7s/#source-backed-comparison-points" class="hash-link" aria-label="Direct link to Source-Backed Comparison Points" title="Direct link to Source-Backed Comparison Points" translate="no">​</a></h2>
<p>GitHub's own <a href="https://docs.github.com/en/pages" target="_blank" rel="noopener noreferrer" class="">Pages documentation</a> makes the core boundary clear: GitHub Pages is a publishing feature for websites from a repository. That is ideal for docs, project pages, and static marketing sites. It is also the reason the article should not frame Pages as weak; it is excellent at the static publishing job it was designed to do.</p>
<p>The W7S argument starts when the repository needs more than static files. W7S keeps the release path in GitHub through <a class="" href="https://w7s.io/docs/deploy-from-github/">deploys from GitHub Actions</a>, but it can package a static frontend and native backend from the same repo. The <a class="" href="https://w7s.io/docs/project-layouts/">project layout docs</a> are the important source here because they show how W7S detects frontend output and backend entrypoints without asking the team to split the app into separate platforms.</p>
<p>Custom domains are another practical difference. GitHub documents <a href="https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site" target="_blank" rel="noopener noreferrer" class="">custom domain configuration for Pages</a>, and W7S uses a similar repository-visible idea with <code>CNAME</code> plus DNS authorization in its <a class="" href="https://w7s.io/docs/custom-domains/">custom domains docs</a>. The W7S advantage is not merely owning a hostname; it is keeping that hostname attached to an app that can also have backend routes and bindings.</p>
<p>Once a site needs forms, search, uploads, status endpoints, or per-branch test data, W7S provides the next layer directly. <a class="" href="https://w7s.io/docs/urls-and-routing/">URLs and routing</a> describe the owner/repository URL model, while <a class="" href="https://w7s.io/docs/storage-bindings/">storage bindings</a> explain how databases, key-value stores, and file buckets can be declared by the app. That gives a growing Pages-style site a path to become a small application without creating a separate backend product.</p>
<p>The right conclusion is measured. Stay on GitHub Pages when static publishing is the whole requirement. Move to W7S when the repository should keep its GitHub-native release flow but also needs API routes, persistent state, queues, schedules, workflows, or branch-isolated runtime resources.</p>
<p>This does not make GitHub Pages obsolete. It keeps Pages in its lane and gives growing projects a next step without leaving GitHub as the release surface.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="static-deploys-stay-simple">Static Deploys Stay Simple<a href="https://w7s.io/docs/blog/replacing-github-pages-with-w7s/#static-deploys-stay-simple" class="hash-link" aria-label="Direct link to Static Deploys Stay Simple" title="Direct link to Static Deploys Stay Simple" translate="no">​</a></h2>
<p>A W7S static deploy can still be boring:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">.github/workflows/deploy.yml</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Deploy</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">on</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">push</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">branches</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">main</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">jobs</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">deploy</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">runs-on</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> ubuntu</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">latest</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">permissions</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">contents</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> read</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">id-token</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> write</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">steps</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> actions/checkout@v5</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> actions/setup</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">node@v6</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token key atrule">node-version</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">22</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> npm ci</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> npm run build</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> w7s</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">io/w7s</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">cloud@v1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token key atrule">token</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> github.token </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>If the build output is <code>dist/</code>, <code>build/</code>, or another supported output directory, W7S publishes the static assets.</p>
<p>The difference is what the repository can add later without changing platform class.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="add-a-backend-without-moving-the-site">Add a Backend Without Moving the Site<a href="https://w7s.io/docs/blog/replacing-github-pages-with-w7s/#add-a-backend-without-moving-the-site" class="hash-link" aria-label="Direct link to Add a Backend Without Moving the Site" title="Direct link to Add a Backend Without Moving the Site" translate="no">​</a></h2>
<p>With GitHub Pages, dynamic behavior usually means calling an external API. That can be fine, but the project now has two deployment surfaces: the static site and the backend.</p>
<p>W7S can deploy both from the same repository:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">frontend/</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  package.json</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  dist/</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">backend/</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  index.ts</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">w7s.json</span><br></div></code></pre></div></div>
<p>The backend is a normal request handler:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/index.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">async</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token operator">:</span><span class="token plain"> Request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> env</span><span class="token operator">:</span><span class="token plain"> Env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> url </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name constant" style="color:rgb(189, 147, 249)">URL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">pathname </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/api/status"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        ok</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        repository</span><span class="token operator">:</span><span class="token plain"> env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">W7S_REPOSITORY</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        environment</span><span class="token operator">:</span><span class="token plain"> env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">W7S_ENVIRONMENT</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Not found"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">status</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">404</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>Now the same app can serve static pages and backend routes.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="add-storage-when-static-state-is-not-enough">Add Storage When Static State Is Not Enough<a href="https://w7s.io/docs/blog/replacing-github-pages-with-w7s/#add-storage-when-static-state-is-not-enough" class="hash-link" aria-label="Direct link to Add Storage When Static State Is Not Enough" title="Direct link to Add Storage When Static State Is Not Enough" translate="no">​</a></h2>
<p>Static sites often begin with JSON files committed to the repo. That works until the data becomes user-created, generated, private, or large.</p>
<p>W7S lets the app declare managed storage:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"bindings"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"db"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"binding"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"DB"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"migrations"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"migrations"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"kv"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"CACHE"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"fs"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"FILES"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Use:</p>
<ul>
<li class="">Serverless DB for notes, forms, metadata, and app-local relational data;</li>
<li class="">KV for small cached values and latest state;</li>
<li class="">FS for uploaded files or generated artifacts.</li>
</ul>
<p>The repo explains its runtime needs. The site does not have to bolt on a separate backend platform just to store a row.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="add-background-work-without-a-worker-server">Add Background Work Without a Worker Server<a href="https://w7s.io/docs/blog/replacing-github-pages-with-w7s/#add-background-work-without-a-worker-server" class="hash-link" aria-label="Direct link to Add Background Work Without a Worker Server" title="Direct link to Add Background Work Without a Worker Server" translate="no">​</a></h2>
<p>Once a static site has backend routes, it often needs background behavior:</p>
<ul>
<li class="">send an email after form submission;</li>
<li class="">regenerate a search index;</li>
<li class="">refresh data from an external API;</li>
<li class="">process an uploaded file;</li>
<li class="">run a daily cleanup.</li>
</ul>
<p>W7S covers those cases with queues and schedules:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"queues"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"jobs"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"schedules"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"cron"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"0 0 * * *"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"path"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/_w7s/schedules/daily"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The backend receives queue and schedule deliveries at declared paths. There is no separate worker service to run beside the static site.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="custom-domains-stay-reviewable">Custom Domains Stay Reviewable<a href="https://w7s.io/docs/blog/replacing-github-pages-with-w7s/#custom-domains-stay-reviewable" class="hash-link" aria-label="Direct link to Custom Domains Stay Reviewable" title="Direct link to Custom Domains Stay Reviewable" translate="no">​</a></h2>
<p>GitHub Pages supports custom domains. W7S keeps the same repository-friendly idea with a <code>CNAME</code> file:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">CNAME</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">docs.example.com</span><br></div></code></pre></div></div>
<p>W7S also supports DNS authorization records that allow a domain owner to control which GitHub owner or repository may claim names under a zone.</p>
<p>The repository requests the hostname. DNS authorizes it. The platform maps it.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="branches-can-preview-runtime-changes">Branches Can Preview Runtime Changes<a href="https://w7s.io/docs/blog/replacing-github-pages-with-w7s/#branches-can-preview-runtime-changes" class="hash-link" aria-label="Direct link to Branches Can Preview Runtime Changes" title="Direct link to Branches Can Preview Runtime Changes" translate="no">​</a></h2>
<p>GitHub Pages is excellent at publishing one static output. W7S branch environments are more useful when a branch changes runtime behavior:</p>
<ul>
<li class="">new backend route;</li>
<li class="">new database migration;</li>
<li class="">new queue;</li>
<li class="">new schedule;</li>
<li class="">changed environment variable list;</li>
<li class="">changed custom domain declaration.</li>
</ul>
<p>Non-production branches get branch-derived environments and URLs. Managed resources can be scoped by repository and environment, making previews more honest than a static artifact alone.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="when-github-pages-still-fits-better">When GitHub Pages Still Fits Better<a href="https://w7s.io/docs/blog/replacing-github-pages-with-w7s/#when-github-pages-still-fits-better" class="hash-link" aria-label="Direct link to When GitHub Pages Still Fits Better" title="Direct link to When GitHub Pages Still Fits Better" translate="no">​</a></h2>
<p>Keep GitHub Pages when the site is:</p>
<ul>
<li class="">purely static;</li>
<li class="">documentation, project pages, or a personal site;</li>
<li class="">intentionally free of backend routes and storage;</li>
<li class="">already using a Pages-specific workflow that is good enough;</li>
<li class="">better served by GitHub's built-in Pages settings.</li>
</ul>
<p>There is no reason to add an app platform when static hosting is the whole need.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="recommendation">Recommendation<a href="https://w7s.io/docs/blog/replacing-github-pages-with-w7s/#recommendation" class="hash-link" aria-label="Direct link to Recommendation" title="Direct link to Recommendation" translate="no">​</a></h2>
<p>Start with GitHub Pages when the project is just a static site.</p>
<p>Move to W7S when the repository starts needing app behavior:</p>
<ul>
<li class="">backend routes;</li>
<li class="">storage bindings;</li>
<li class="">queues or schedules;</li>
<li class="">branch-isolated previews;</li>
<li class="">internal service calls;</li>
<li class="">a runtime contract in <code>w7s.json</code>.</li>
</ul>
<p>The migration path is natural because both models start from GitHub. W7S keeps that source of truth and adds the app runtime GitHub Pages deliberately does not try to be.</p>]]></content:encoded>
            <category>platforms</category>
            <category>static-sites</category>
            <category>github-actions</category>
            <category>migration</category>
        </item>
        <item>
            <title><![CDATA[Replacing Heroku, Render, Railway, and Fly.io With W7S]]></title>
            <link>https://w7s.io/docs/blog/replacing-heroku-render-railway-and-fly-with-w7s/</link>
            <guid>https://w7s.io/docs/blog/replacing-heroku-render-railway-and-fly-with-w7s/</guid>
            <pubDate>Sun, 31 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[How to replace process-oriented app hosting with W7S when the app fits static assets, native backends, managed bindings, queues, schedules, and workflows.]]></description>
            <content:encoded><![CDATA[<p>Heroku, Render, Railway, and Fly.io are good platforms when an application needs a process: a web server, a worker, a container image, a machine, or a long-running service with operational controls.</p>
<p>Many small apps do not need that much runtime. They need a frontend, a few backend routes, a database, a cache, file storage, maybe a background job, maybe a schedule, and a clean deploy path from GitHub.</p>
<p>For that shape, W7S can replace a process platform with a repository-native app platform.</p>
<blockquote>
<p>If the app can be a request handler plus managed bindings, do not start by operating services.</p>
</blockquote>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-short-mapping">The Short Mapping<a href="https://w7s.io/docs/blog/replacing-heroku-render-railway-and-fly-with-w7s/#the-short-mapping" class="hash-link" aria-label="Direct link to The Short Mapping" title="Direct link to The Short Mapping" translate="no">​</a></h2>
<table><thead><tr><th>Process platform concept</th><th>W7S replacement</th><th>Best fit</th></tr></thead><tbody><tr><td>Web process / web service / machine</td><td>Native backend</td><td>HTTP request handling without an always-on service</td></tr><tr><td>Static file serving</td><td>Static asset deploy</td><td>Frontend and docs output</td></tr><tr><td>Worker process</td><td>Backend queues</td><td>Async jobs delivered to backend routes</td></tr><tr><td>Cron process / scheduled job</td><td>Backend schedules</td><td>Time-based work</td></tr><tr><td>Release phase / one-off task</td><td>GitHub Actions step or workflow route</td><td>Build-time and controlled operational work</td></tr><tr><td>Add-on database</td><td>Serverless DB or Postgres binding</td><td>App-local SQL or external Postgres</td></tr><tr><td>Redis add-on</td><td>KV or Stateful Objects</td><td>Cache, latest state, per-key state</td></tr><tr><td>Object storage add-on</td><td>FS binding</td><td>Files and larger payloads</td></tr><tr><td>Procfile / service command</td><td>Backend entrypoint and <code>w7s.json</code></td><td>Runtime contract in repo</td></tr><tr><td>Platform environment vars</td><td>W7S vars and secrets</td><td>GitHub-provided runtime values</td></tr><tr><td>Dashboard deploy</td><td>GitHub Actions deploy</td><td>Auditable release flow</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="source-backed-comparison-points">Source-Backed Comparison Points<a href="https://w7s.io/docs/blog/replacing-heroku-render-railway-and-fly-with-w7s/#source-backed-comparison-points" class="hash-link" aria-label="Direct link to Source-Backed Comparison Points" title="Direct link to Source-Backed Comparison Points" translate="no">​</a></h2>
<p>The platforms in this comparison are strong because they run process-oriented applications well. Heroku documents <a href="https://devcenter.heroku.com/articles/dynos" target="_blank" rel="noopener noreferrer" class="">dynos</a> and <a href="https://devcenter.heroku.com/articles/procfile" target="_blank" rel="noopener noreferrer" class="">Procfile process types</a>; Render documents <a href="https://render.com/docs/web-services" target="_blank" rel="noopener noreferrer" class="">web services</a> and <a href="https://render.com/docs/background-workers" target="_blank" rel="noopener noreferrer" class="">background workers</a>; Railway documents projects, services, environments, and deployments in its <a href="https://docs.railway.com/platform" target="_blank" rel="noopener noreferrer" class="">platform docs</a>; and Fly.io exposes low-level control through <a href="https://fly.io/docs/machines/" target="_blank" rel="noopener noreferrer" class="">Fly Machines</a>. Those are useful models when the app needs a real process, container, machine, or worker lifecycle.</p>
<p>W7S competes in a narrower and more specific lane. If the app is static assets plus a JavaScript or TypeScript backend handler, <a class="" href="https://w7s.io/docs/project-layouts/">project layouts</a> and <a class="" href="https://w7s.io/docs/deploy-from-github/">deploys from GitHub</a> can replace the service-definition step. The repository builds in CI, W7S receives the archive, and the runtime dispatches HTTP requests without the team choosing a process size, start command, port, machine count, or idle service behavior.</p>
<p>Background work is where the distinction becomes operationally important. Process platforms commonly model async work as another long-running worker process. W7S models the common small-app version as <a class="" href="https://w7s.io/docs/backend-queues/">backend queues</a>, <a class="" href="https://w7s.io/docs/backend-schedules/">backend schedules</a>, and <a class="" href="https://w7s.io/docs/backend-workflows/">backend workflows</a>. That removes a class of service management when the work can be delivered to a route or coordinated as a durable platform workflow.</p>
<p>Storage follows the same pattern. Instead of treating a database, cache, and file bucket as separate add-ons or external services for every app, W7S lets the repository declare resources with <a class="" href="https://w7s.io/docs/storage-bindings/">storage bindings</a> and <a class="" href="https://w7s.io/docs/serverless-database/">serverless database</a>. For small products, that is often the difference between shipping an app and operating a set of support services around the app.</p>
<p>The honest boundary is containers. If a project needs arbitrary processes, custom images, private networking details, long-lived daemons, or VM-level controls, Heroku, Render, Railway, or Fly.io may remain the better fit. If the process is mostly incidental and the app naturally fits request handlers, static assets, managed bindings, queues, schedules, and workflows, W7S is a real replacement with less operational surface.</p>
<p>This is not a claim that W7S replaces containers. It replaces a common subset of process-platform usage where the process was mostly a packaging detail.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="processes-become-request-handlers">Processes Become Request Handlers<a href="https://w7s.io/docs/blog/replacing-heroku-render-railway-and-fly-with-w7s/#processes-become-request-handlers" class="hash-link" aria-label="Direct link to Processes Become Request Handlers" title="Direct link to Processes Become Request Handlers" translate="no">​</a></h2>
<p>On process platforms, the application usually runs a web command:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">Procfile</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">web: npm start</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">worker: npm run worker</span><br></div></code></pre></div></div>
<p>or a service definition that eventually starts a long-running server.</p>
<p>In W7S, a native backend exports a request handler:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/index.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">async</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token operator">:</span><span class="token plain"> Request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> env</span><span class="token operator">:</span><span class="token plain"> Env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> url </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name constant" style="color:rgb(189, 147, 249)">URL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">pathname </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/api/health"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">ok</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">pathname </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/api/orders"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">handleOrders</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Not found"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">status</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">404</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>There is no port to bind, process size to choose, idle machine to keep warm, or web dyno count to scale. W7S owns the runtime and dispatches requests to the backend.</p>
<p>That is a better fit for apps whose backend work is naturally request/response.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="workers-become-queues">Workers Become Queues<a href="https://w7s.io/docs/blog/replacing-heroku-render-railway-and-fly-with-w7s/#workers-become-queues" class="hash-link" aria-label="Direct link to Workers Become Queues" title="Direct link to Workers Become Queues" translate="no">​</a></h2>
<p>Process platforms often push background work into a worker process. That worker then needs a queue, credentials, scaling rules, logs, retry behavior, and deployment coordination.</p>
<p>W7S makes the queue part of the app contract:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"queues"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"jobs"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"consumer"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/_w7s/queues/jobs"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Producers enqueue through <code>W7S_QUEUE</code>:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/jobs.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">enqueueEmail</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">env</span><span class="token operator">:</span><span class="token plain"> Env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> payload</span><span class="token operator">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">unknown</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">W7S_QUEUE</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token string" style="color:rgb(255, 121, 198)">"https://w7s.internal/api/v1/queues/acme/app/jobs"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      method</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"POST"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      headers</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        authorization</span><span class="token operator">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token template-string string" style="color:rgb(255, 121, 198)">Bearer </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">${</span><span class="token template-string interpolation">env</span><span class="token template-string interpolation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token template-string interpolation constant" style="color:rgb(189, 147, 249)">W7S_QUEUE_TOKEN</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token string-property property">"content-type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"application/json"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      body</span><span class="token operator">:</span><span class="token plain"> </span><span class="token constant" style="color:rgb(189, 147, 249)">JSON</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">stringify</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">payload</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The backend consumes batches through a normal route:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/index.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">pathname </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/_w7s/queues/jobs"</span><span class="token plain"> </span><span class="token operator">&amp;&amp;</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">method </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"POST"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> batch </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">for</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> message </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">of</span><span class="token plain"> batch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">messages</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">sendEmail</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">message</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">body</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">ok</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> processed</span><span class="token operator">:</span><span class="token plain"> batch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">messages</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">length</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The app owns the code. W7S owns the delivery mechanism.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="scheduled-jobs-become-schedules">Scheduled Jobs Become Schedules<a href="https://w7s.io/docs/blog/replacing-heroku-render-railway-and-fly-with-w7s/#scheduled-jobs-become-schedules" class="hash-link" aria-label="Direct link to Scheduled Jobs Become Schedules" title="Direct link to Scheduled Jobs Become Schedules" translate="no">​</a></h2>
<p>A process platform may use a cron add-on, scheduled job service, or always-on worker loop.</p>
<p>W7S schedules are declared next to the backend:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"schedules"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"cron"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"0 * * * *"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"path"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/_w7s/schedules/hourly"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The schedule invokes the backend route:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/index.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">pathname </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/_w7s/schedules/hourly"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">refreshReports</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">ok</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>That removes a separate scheduler service for jobs that already belong to the app.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="add-ons-become-bindings">Add-Ons Become Bindings<a href="https://w7s.io/docs/blog/replacing-heroku-render-railway-and-fly-with-w7s/#add-ons-become-bindings" class="hash-link" aria-label="Direct link to Add-Ons Become Bindings" title="Direct link to Add-Ons Become Bindings" translate="no">​</a></h2>
<p>Heroku-style platforms popularized the add-on model. That works well, but it can scatter the runtime contract across a dashboard, environment variables, provider credentials, and app code.</p>
<p>W7S uses <code>w7s.json</code>:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"bindings"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"db"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"binding"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"DB"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"migrations"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"migrations"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"kv"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"CACHE"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"fs"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"FILES"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"vars"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"PUBLIC_API_ORIGIN"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"secrets"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"STRIPE_SECRET_KEY"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The app receives the declared resources through <code>env</code>. Storage is scoped by repository and environment, so a feature branch does not need to share production resource names.</p>
<p>Use the right primitive:</p>
<table><thead><tr><th>Need</th><th>W7S component</th></tr></thead><tbody><tr><td>Relational app data</td><td>Serverless DB</td></tr><tr><td>Existing Postgres</td><td>Managed Postgres binding</td></tr><tr><td>Cache or latest value</td><td>KV</td></tr><tr><td>Files and larger payloads</td><td>FS</td></tr><tr><td>Per-entity state and compute</td><td>Stateful Objects</td></tr><tr><td>Async work</td><td>Queues</td></tr><tr><td>Durable process</td><td>Workflows</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="deployment-becomes-a-github-workflow">Deployment Becomes a GitHub Workflow<a href="https://w7s.io/docs/blog/replacing-heroku-render-railway-and-fly-with-w7s/#deployment-becomes-a-github-workflow" class="hash-link" aria-label="Direct link to Deployment Becomes a GitHub Workflow" title="Direct link to Deployment Becomes a GitHub Workflow" translate="no">​</a></h2>
<p>Process platforms often expose multiple deploy paths: Git push, dashboard deploy, CLI deploy, container deploy, or GitHub integration.</p>
<p>W7S keeps one boring default:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">.github/workflows/deploy.yml</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> actions/checkout@v5</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> actions/setup</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">node@v6</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">node-version</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">22</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> npm ci</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> npm run build</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> w7s</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">io/w7s</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">cloud@v1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">token</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> github.token </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>If the app needs tests, migrations, type checks, generated files, or a custom build directory, put those steps in the workflow. The release behavior is code-reviewed with the app.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-you-give-up">What You Give Up<a href="https://w7s.io/docs/blog/replacing-heroku-render-railway-and-fly-with-w7s/#what-you-give-up" class="hash-link" aria-label="Direct link to What You Give Up" title="Direct link to What You Give Up" translate="no">​</a></h2>
<p>W7S is not a process platform. Do not replace Heroku, Render, Railway, or Fly.io with W7S if the app needs:</p>
<ul>
<li class="">arbitrary containers;</li>
<li class="">long-running daemons;</li>
<li class="">custom TCP listeners;</li>
<li class="">WebSocket-heavy always-on services that need process-level control;</li>
<li class="">language runtimes outside the W7S native backend model;</li>
<li class="">private networks with process services;</li>
<li class="">manual machine sizing and placement;</li>
<li class="">shell access into a running container;</li>
<li class="">process supervisors or multi-process containers.</li>
</ul>
<p>Those are real platform needs. Use a process platform when the process is the product.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="recommendation">Recommendation<a href="https://w7s.io/docs/blog/replacing-heroku-render-railway-and-fly-with-w7s/#recommendation" class="hash-link" aria-label="Direct link to Recommendation" title="Direct link to Recommendation" translate="no">​</a></h2>
<p>Use W7S when the app can be reduced to:</p>
<ul>
<li class="">static assets;</li>
<li class="">JavaScript/TypeScript request handlers;</li>
<li class="">managed DB, KV, FS, queues, schedules, and workflows;</li>
<li class="">GitHub Actions as the release boundary;</li>
<li class="">branch environments instead of long-lived staging services.</li>
</ul>
<p>Keep a process platform when the app genuinely needs a process.</p>
<p>The important distinction is not "simple versus serious." It is runtime shape. A serious app can be static assets plus request handlers and managed bindings. For that shape, W7S removes service management that was never central to the product.</p>]]></content:encoded>
            <category>platforms</category>
            <category>alternatives</category>
            <category>backends</category>
            <category>queues</category>
        </item>
        <item>
            <title><![CDATA[Replacing Kubernetes for Small Apps With W7S]]></title>
            <link>https://w7s.io/docs/blog/replacing-kubernetes-for-small-apps-with-w7s/</link>
            <guid>https://w7s.io/docs/blog/replacing-kubernetes-for-small-apps-with-w7s/</guid>
            <pubDate>Sun, 31 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Kubernetes is excellent infrastructure for container fleets. W7S is a smaller default when an app only needs static assets, request handlers, storage bindings, queues, schedules, and workflows.]]></description>
            <content:encoded><![CDATA[<p>Kubernetes is powerful because it can run almost anything. Pods, Deployments, Services, Ingress, Jobs, CronJobs, ConfigMaps, Secrets, volumes, operators, service meshes, and controllers can model a huge range of systems.</p>
<p>That flexibility is also why Kubernetes is often too much for small apps.</p>
<p>Many apps do not need a cluster. They need:</p>
<ul>
<li class="">a frontend;</li>
<li class="">a few backend routes;</li>
<li class="">a database;</li>
<li class="">file storage;</li>
<li class="">a background queue;</li>
<li class="">a schedule;</li>
<li class="">a durable workflow;</li>
<li class="">a deploy path from GitHub;</li>
<li class="">logs and usage feedback.</li>
</ul>
<p>W7S is designed for that smaller shape.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-short-mapping">The Short Mapping<a href="https://w7s.io/docs/blog/replacing-kubernetes-for-small-apps-with-w7s/#the-short-mapping" class="hash-link" aria-label="Direct link to The Short Mapping" title="Direct link to The Short Mapping" translate="no">​</a></h2>
<table><thead><tr><th>Kubernetes concept</th><th>W7S replacement</th><th>Best fit</th></tr></thead><tbody><tr><td>Cluster</td><td>W7S cloud or self-hosted W7S</td><td>Shared app platform</td></tr><tr><td>Namespace</td><td>GitHub owner/repo/environment</td><td>App and branch isolation</td></tr><tr><td>Deployment</td><td>W7S deploy</td><td>Static assets and native backend</td></tr><tr><td>Pod / container</td><td>Native backend request handler</td><td>HTTP app logic</td></tr><tr><td>Service</td><td>W7S owner/repo URL or Backend RPC</td><td>Public and internal routing</td></tr><tr><td>Ingress</td><td>W7S routing and custom domains</td><td>HTTP routing without ingress config</td></tr><tr><td>ConfigMap</td><td><code>w7s.json</code> vars or app storage</td><td>Runtime configuration</td></tr><tr><td>Secret</td><td>GitHub secrets plus W7S secret bindings</td><td>Deploy-time secrets</td></tr><tr><td>PersistentVolume</td><td>DB, KV, FS, or Stateful Objects</td><td>App-level state</td></tr><tr><td>Job</td><td>Queue message or workflow</td><td>Background work</td></tr><tr><td>CronJob</td><td>W7S schedule</td><td>Time-based work</td></tr><tr><td>Operator</td><td>W7S managed binding provisioning</td><td>Common platform resources</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="source-backed-comparison-points">Source-Backed Comparison Points<a href="https://w7s.io/docs/blog/replacing-kubernetes-for-small-apps-with-w7s/#source-backed-comparison-points" class="hash-link" aria-label="Direct link to Source-Backed Comparison Points" title="Direct link to Source-Backed Comparison Points" translate="no">​</a></h2>
<p>Kubernetes is broad infrastructure, not a small-app deployment product. Its official docs cover <a href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/" target="_blank" rel="noopener noreferrer" class="">Deployments</a>, <a href="https://kubernetes.io/docs/concepts/services-networking/service/" target="_blank" rel="noopener noreferrer" class="">Services</a>, <a href="https://kubernetes.io/docs/concepts/services-networking/ingress/" target="_blank" rel="noopener noreferrer" class="">Ingress</a>, <a href="https://kubernetes.io/docs/concepts/workloads/controllers/job/" target="_blank" rel="noopener noreferrer" class="">Jobs</a>, and <a href="https://kubernetes.io/docs/concepts/workloads/controllers/cron-jobs/" target="_blank" rel="noopener noreferrer" class="">CronJobs</a> as separate concepts. That separation is powerful for platform teams, but it is a lot of surface area for a small app that only needs a frontend, a few routes, and some managed state.</p>
<p>W7S replaces that cluster vocabulary with a repository vocabulary. <a class="" href="https://w7s.io/docs/deploy-from-github/">Deploying from GitHub</a> defines the release mechanism, <a class="" href="https://w7s.io/docs/project-layouts/">project layouts</a> define what the archive can contain, and <a class="" href="https://w7s.io/docs/urls-and-routing/">URLs and routing</a> define how owner/repo identity becomes a public app URL. The result is less flexible than Kubernetes, but much easier to standardize for apps that fit the model.</p>
<p>For background work, Kubernetes has Jobs and CronJobs. W7S maps the small-app cases to <a class="" href="https://w7s.io/docs/backend-queues/">backend queues</a>, <a class="" href="https://w7s.io/docs/backend-schedules/">backend schedules</a>, and <a class="" href="https://w7s.io/docs/backend-workflows/">backend workflows</a>. That means the team can express async work as app behavior instead of maintaining queue workers, job manifests, schedule objects, service accounts, and cluster-level operational policy for every small service.</p>
<p>State also moves from infrastructure objects to app bindings. Kubernetes can support persistent volumes, operators, and external managed databases, but that usually creates more platform decisions. W7S <a class="" href="https://w7s.io/docs/storage-bindings/">storage bindings</a> and <a class="" href="https://w7s.io/docs/serverless-database/">serverless database</a> give a smaller default: declare the app resource, let the platform provision it per repository and environment, and keep the runtime access path in code.</p>
<p>The replacement claim should stay precise. W7S is not trying to run arbitrary containers, service meshes, custom controllers, or cluster-native workloads. It is a replacement for the recurring case where Kubernetes is being used as a deploy substrate for an app that could be static assets, request handlers, managed bindings, queues, schedules, and workflows.</p>
<p>This is not "Kubernetes is bad." It is "do not turn every small app into a cluster-management problem."</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="containers-become-native-backends">Containers Become Native Backends<a href="https://w7s.io/docs/blog/replacing-kubernetes-for-small-apps-with-w7s/#containers-become-native-backends" class="hash-link" aria-label="Direct link to Containers Become Native Backends" title="Direct link to Containers Become Native Backends" translate="no">​</a></h2>
<p>Kubernetes runs containers. That is the right abstraction when the application needs arbitrary runtimes, process managers, system packages, sidecars, or long-running daemons.</p>
<p>W7S native backends use a narrower contract:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/index.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">async</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token operator">:</span><span class="token plain"> Request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> env</span><span class="token operator">:</span><span class="token plain"> Env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> ctx</span><span class="token operator">:</span><span class="token plain"> ExecutionContext</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> url </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name constant" style="color:rgb(189, 147, 249)">URL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">pathname </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/api/health"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">ok</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Not found"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">status</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">404</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>The benefit is that the app no longer owns:</p>
<ul>
<li class="">container images;</li>
<li class="">pod specs;</li>
<li class="">service selectors;</li>
<li class="">ingress resources;</li>
<li class="">health probes;</li>
<li class="">horizontal autoscaling config;</li>
<li class="">node scheduling concerns.</li>
</ul>
<p>If the app fits the backend handler model, that missing surface is a feature.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="services-become-repository-routes">Services Become Repository Routes<a href="https://w7s.io/docs/blog/replacing-kubernetes-for-small-apps-with-w7s/#services-become-repository-routes" class="hash-link" aria-label="Direct link to Services Become Repository Routes" title="Direct link to Services Become Repository Routes" translate="no">​</a></h2>
<p>Kubernetes separates pods from Services and exposes traffic through Service and Ingress resources.</p>
<p>W7S uses repository identity:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">https://acme.w7s.cloud/checkout/</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">https://feature-payments--acme.w7s.cloud/checkout/</span><br></div></code></pre></div></div>
<p>Internal service calls use Backend RPC:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/auth.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> response </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">W7S_RPC</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token string" style="color:rgb(255, 121, 198)">"https://w7s.internal/api/v1/rpc/acme/auth/session"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    headers</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      authorization</span><span class="token operator">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token template-string string" style="color:rgb(255, 121, 198)">Bearer </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">${</span><span class="token template-string interpolation">env</span><span class="token template-string interpolation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token template-string interpolation constant" style="color:rgb(189, 147, 249)">W7S_RPC_TOKEN</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>That is not as general as Kubernetes networking, but it is enough for many HTTP-shaped app systems. The service boundary is the repository.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="configmaps-and-secrets-move-to-the-deploy-contract">ConfigMaps and Secrets Move to the Deploy Contract<a href="https://w7s.io/docs/blog/replacing-kubernetes-for-small-apps-with-w7s/#configmaps-and-secrets-move-to-the-deploy-contract" class="hash-link" aria-label="Direct link to ConfigMaps and Secrets Move to the Deploy Contract" title="Direct link to ConfigMaps and Secrets Move to the Deploy Contract" translate="no">​</a></h2>
<p>Kubernetes uses ConfigMaps and Secrets as API objects. W7S keeps common app configuration in the repository and GitHub workflow:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"vars"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"PUBLIC_API_ORIGIN"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"secrets"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"STRIPE_SECRET_KEY"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">.github/workflows/deploy.yml</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> w7s</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">io/w7s</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">cloud@v1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">PUBLIC_API_ORIGIN</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> vars.PUBLIC_API_ORIGIN </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">STRIPE_SECRET_KEY</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> secrets.STRIPE_SECRET_KEY </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">token</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> github.token </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The app reads values from <code>env</code>. Deploy summaries can count secrets without exposing secret values.</p>
<p>For dynamic product configuration, use KV or DB rather than redeploying platform config.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="persistent-volumes-become-app-storage-bindings">Persistent Volumes Become App Storage Bindings<a href="https://w7s.io/docs/blog/replacing-kubernetes-for-small-apps-with-w7s/#persistent-volumes-become-app-storage-bindings" class="hash-link" aria-label="Direct link to Persistent Volumes Become App Storage Bindings" title="Direct link to Persistent Volumes Become App Storage Bindings" translate="no">​</a></h2>
<p>Kubernetes volumes are infrastructure-level storage. Many small apps need app-level storage instead:</p>
<ul>
<li class="">SQL rows;</li>
<li class="">cache entries;</li>
<li class="">uploaded files;</li>
<li class="">per-entity durable state.</li>
</ul>
<p>W7S declares those directly:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"bindings"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"db"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"binding"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"DB"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"migrations"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"migrations"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"kv"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"CACHE"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"fs"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"FILES"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"durableObjects"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"binding"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"SESSION"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"className"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Session"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>This keeps storage at the level the application understands. A small app usually should not need to pick a storage class before it can save user settings.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="jobs-and-cronjobs-become-queues-schedules-and-workflows">Jobs and CronJobs Become Queues, Schedules, and Workflows<a href="https://w7s.io/docs/blog/replacing-kubernetes-for-small-apps-with-w7s/#jobs-and-cronjobs-become-queues-schedules-and-workflows" class="hash-link" aria-label="Direct link to Jobs and CronJobs Become Queues, Schedules, and Workflows" title="Direct link to Jobs and CronJobs Become Queues, Schedules, and Workflows" translate="no">​</a></h2>
<p>Kubernetes Jobs and CronJobs are useful for containerized work. W7S maps app-level background work to platform primitives:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"queues"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"jobs"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"schedules"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"cron"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"0 * * * *"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"path"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/_w7s/schedules/hourly"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"workflows"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"checkout"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Use:</p>
<ul>
<li class="">queues for async work that can retry;</li>
<li class="">schedules for time-based triggers;</li>
<li class="">workflows for durable business processes with status.</li>
</ul>
<p>This is less general than running any container as a job, but it fits the common app cases without introducing cluster resources.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="operators-become-platform-provisioning">Operators Become Platform Provisioning<a href="https://w7s.io/docs/blog/replacing-kubernetes-for-small-apps-with-w7s/#operators-become-platform-provisioning" class="hash-link" aria-label="Direct link to Operators Become Platform Provisioning" title="Direct link to Operators Become Platform Provisioning" translate="no">​</a></h2>
<p>Kubernetes operators are powerful because they encode domain-specific operations into the cluster.</p>
<p>For small apps, many operator-like needs are common enough that W7S can own them:</p>
<ul>
<li class="">create a DB binding;</li>
<li class="">apply SQL migrations;</li>
<li class="">create a KV namespace;</li>
<li class="">create a file bucket;</li>
<li class="">wire a queue to a backend route;</li>
<li class="">wire a schedule to a backend route;</li>
<li class="">wire a workflow to a backend route;</li>
<li class="">inject runtime bindings;</li>
<li class="">account for usage by repository and environment.</li>
</ul>
<p>The app declares intent in <code>w7s.json</code>; W7S handles the provisioning path.</p>
<p>That is not a replacement for custom operators. It is a replacement for needing operators before the app has operator-scale needs.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="github-actions-replaces-the-release-pipeline-layer">GitHub Actions Replaces the Release Pipeline Layer<a href="https://w7s.io/docs/blog/replacing-kubernetes-for-small-apps-with-w7s/#github-actions-replaces-the-release-pipeline-layer" class="hash-link" aria-label="Direct link to GitHub Actions Replaces the Release Pipeline Layer" title="Direct link to GitHub Actions Replaces the Release Pipeline Layer" translate="no">​</a></h2>
<p>Kubernetes deploys often grow a release stack:</p>
<ul>
<li class="">image build;</li>
<li class="">registry push;</li>
<li class="">manifest render;</li>
<li class="">Helm or Kustomize;</li>
<li class="">kubectl apply;</li>
<li class="">rollout status;</li>
<li class="">environment promotion;</li>
<li class="">secret management.</li>
</ul>
<p>W7S keeps the default smaller:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">.github/workflows/deploy.yml</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> actions/checkout@v5</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> actions/setup</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">node@v6</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">node-version</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">22</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> npm ci</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> npm run build</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> w7s</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">io/w7s</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">cloud@v1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">token</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> github.token </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>For apps that fit the W7S runtime, this removes entire categories of release machinery.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-kubernetes-still-does-better">What Kubernetes Still Does Better<a href="https://w7s.io/docs/blog/replacing-kubernetes-for-small-apps-with-w7s/#what-kubernetes-still-does-better" class="hash-link" aria-label="Direct link to What Kubernetes Still Does Better" title="Direct link to What Kubernetes Still Does Better" translate="no">​</a></h2>
<p>Use Kubernetes when the app or organization needs:</p>
<ul>
<li class="">arbitrary containers and languages;</li>
<li class="">long-running processes;</li>
<li class="">private cluster networking;</li>
<li class="">service mesh behavior;</li>
<li class="">custom controllers and operators;</li>
<li class="">GPU workloads;</li>
<li class="">stateful sets and storage classes;</li>
<li class="">TCP or non-HTTP services;</li>
<li class="">strict multi-tenant cluster policy;</li>
<li class="">existing platform engineering built around Kubernetes;</li>
<li class="">workloads that must run inside a specific VPC or cloud account.</li>
</ul>
<p>Those needs are real. W7S should not pretend to cover them.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="recommendation">Recommendation<a href="https://w7s.io/docs/blog/replacing-kubernetes-for-small-apps-with-w7s/#recommendation" class="hash-link" aria-label="Direct link to Recommendation" title="Direct link to Recommendation" translate="no">​</a></h2>
<p>Use W7S for small apps that can be:</p>
<ul>
<li class="">static assets;</li>
<li class="">JavaScript/TypeScript native backends;</li>
<li class="">managed storage bindings;</li>
<li class="">queues, schedules, and workflows;</li>
<li class="">GitHub Actions deploys;</li>
<li class="">branch environments.</li>
</ul>
<p>Use Kubernetes when the workload truly needs a cluster.</p>
<p>The mistake is not choosing Kubernetes. The mistake is choosing Kubernetes before the application has Kubernetes-shaped problems.</p>]]></content:encoded>
            <category>platforms</category>
            <category>kubernetes</category>
            <category>alternatives</category>
            <category>workflows</category>
        </item>
        <item>
            <title><![CDATA[Replacing NATS With W7S Components]]></title>
            <link>https://w7s.io/docs/blog/replacing-nats-with-w7s-components/</link>
            <guid>https://w7s.io/docs/blog/replacing-nats-with-w7s-components/</guid>
            <pubDate>Sun, 31 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[How to map NATS-style messaging, request/reply, queues, schedules, durable streams, and event workflows onto W7S-native primitives.]]></description>
            <content:encoded><![CDATA[<p>NATS is a strong general-purpose messaging system. It gives teams subjects, request/reply, publish/subscribe, queue groups, persistence through JetStream, and a fast broker that can sit between many services.</p>
<p>W7S does not need to copy that model to support the same application patterns. In many W7S apps, adding NATS would introduce another control plane, another credential model, another operational surface, and another local development story. The better default is to ask a narrower question:</p>
<blockquote>
<p>Can the same product behavior be built from W7S components we already have?</p>
</blockquote>
<p>For many cases, the answer is yes.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-short-mapping">The Short Mapping<a href="https://w7s.io/docs/blog/replacing-nats-with-w7s-components/#the-short-mapping" class="hash-link" aria-label="Direct link to The Short Mapping" title="Direct link to The Short Mapping" translate="no">​</a></h2>
<table><thead><tr><th>NATS concept</th><th>W7S replacement</th><th>Best fit</th></tr></thead><tbody><tr><td>Request/reply</td><td>Backend RPC</td><td>Synchronous service-to-service calls</td></tr><tr><td>Work queue</td><td>Backend queues</td><td>Async jobs owned by one consumer app</td></tr><tr><td>Pub/sub subject</td><td>Event router plus queues</td><td>Explicit fanout to known consumers</td></tr><tr><td>Queue groups</td><td>One W7S queue consumer path per app</td><td>Work distribution inside a consumer backend</td></tr><tr><td>JetStream durable stream</td><td>DB, KV, or R2 plus queues</td><td>Stored events, replay, audit, large payloads</td></tr><tr><td>Scheduled publish</td><td>Backend schedules</td><td>Time-based event creation</td></tr><tr><td>Long-running orchestration</td><td>Workflows</td><td>Retryable multi-step processes</td></tr><tr><td>Local broker for development</td><td><code>w7s-local</code> plus HTTP fallbacks</td><td>Testing routing and repo boundaries</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="source-backed-comparison-points">Source-Backed Comparison Points<a href="https://w7s.io/docs/blog/replacing-nats-with-w7s-components/#source-backed-comparison-points" class="hash-link" aria-label="Direct link to Source-Backed Comparison Points" title="Direct link to Source-Backed Comparison Points" translate="no">​</a></h2>
<p>NATS is a messaging system with a clear and useful model. Its docs cover <a href="https://docs.nats.io/nats-concepts/core-nats/pubsub" target="_blank" rel="noopener noreferrer" class="">publish-subscribe</a>, <a href="https://docs.nats.io/nats-concepts/core-nats/reqreply" target="_blank" rel="noopener noreferrer" class="">request-reply</a>, <a href="https://docs.nats.io/nats-concepts/core-nats/queue" target="_blank" rel="noopener noreferrer" class="">queue groups</a>, and <a href="https://docs.nats.io/nats-concepts/jetstream" target="_blank" rel="noopener noreferrer" class="">JetStream</a> for persistence and streaming. That is exactly the right tool when the application needs broker semantics across many services, languages, or hosts.</p>
<p>The W7S replacement is intentionally narrower. Synchronous service-to-service calls map to <a class="" href="https://w7s.io/docs/backend-rpc/">Backend RPC</a>, where the caller and target are repository-scoped W7S apps. Async work maps to <a class="" href="https://w7s.io/docs/backend-queues/">backend queues</a>, where a known consumer handles delivered jobs. Time-based production of work maps to <a class="" href="https://w7s.io/docs/backend-schedules/">backend schedules</a>, and longer business processes map to <a class="" href="https://w7s.io/docs/backend-workflows/">backend workflows</a>.</p>
<p>Durability needs a more careful answer than "use queues." If the app needs replayable event history, audit trails, or large payload storage, W7S should pair queue dispatch with <a class="" href="https://w7s.io/docs/serverless-database/">serverless database</a> records or <a class="" href="https://w7s.io/docs/storage-bindings/">storage bindings</a> for object/file payloads. That is not the same as JetStream, but it does cover many product-level event flows where the event log is part of the application's own data model.</p>
<p>Local testing is also different. NATS gives developers a local broker. W7S gives developers <code>w7s-local</code> and HTTP-shaped service boundaries, which are documented in the W7S local examples and work well when the goal is to test RPC and route behavior between repos. That keeps development close to the deployed W7S shape instead of introducing a second messaging topology just for tests.</p>
<p>The honest recommendation is to avoid adding NATS until the app truly needs broker semantics. If the product needs subjects, wildcard subscriptions, queue groups, and streaming as core architecture, use NATS. If it needs W7S apps to call each other, queue jobs, fan out known events, schedule work, and persist product state, W7S components can replace the broker with less platform surface.</p>
<p>This is not a claim that W7S is a drop-in NATS server. It is a design choice: use W7S-native components for app-level messaging, and reserve NATS for projects that truly need broker semantics.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="requestreply-becomes-backend-rpc">Request/Reply Becomes Backend RPC<a href="https://w7s.io/docs/blog/replacing-nats-with-w7s-components/#requestreply-becomes-backend-rpc" class="hash-link" aria-label="Direct link to Request/Reply Becomes Backend RPC" title="Direct link to Request/Reply Becomes Backend RPC" translate="no">​</a></h2>
<p>NATS request/reply is often used when one service needs an answer from another service right now:</p>
<ul>
<li class="">auth service returns a session;</li>
<li class="">billing service returns entitlement state;</li>
<li class="">inventory service returns availability;</li>
<li class="">internal API returns a computed result.</li>
</ul>
<p>That maps cleanly to W7S Backend RPC.</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/index.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">type</span><span class="token plain"> </span><span class="token class-name">Env</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token constant" style="color:rgb(189, 147, 249)">W7S_RPC</span><span class="token operator">:</span><span class="token plain"> Fetcher</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token constant" style="color:rgb(189, 147, 249)">W7S_RPC_TOKEN</span><span class="token operator">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">string</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">async</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token operator">:</span><span class="token plain"> Request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> env</span><span class="token operator">:</span><span class="token plain"> Env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> response </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">W7S_RPC</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token string" style="color:rgb(255, 121, 198)">"https://w7s.internal/api/v1/rpc/acme/auth/session"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        headers</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          authorization</span><span class="token operator">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token template-string string" style="color:rgb(255, 121, 198)">Bearer </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">${</span><span class="token template-string interpolation">env</span><span class="token template-string interpolation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token template-string interpolation constant" style="color:rgb(189, 147, 249)">W7S_RPC_TOKEN</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          cookie</span><span class="token operator">:</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">headers</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">get</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"cookie"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">??</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">""</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token operator">!</span><span class="token plain">response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">ok</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Auth service unavailable"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">status</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">502</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> session </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">session</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>The target backend receives a normal HTTP request at the target path. W7S resolves the caller from the deployment token, loads the target deployment in the same environment, checks cross-owner authorization when needed, and injects caller identity headers into the target request.</p>
<p>This has a few advantages over introducing a broker for request/reply:</p>
<ul>
<li class="">the call path is HTTP-shaped and easy to debug;</li>
<li class="">same-owner apps can call each other without extra manifest work;</li>
<li class="">cross-owner calls stay target-controlled through <code>rpc.allow</code>;</li>
<li class="">usage accounting can attribute the dispatch to the caller app;</li>
<li class="">branch environments naturally call the matching target environment.</li>
</ul>
<p>Use RPC when the caller needs the response before it can finish its own response.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="work-queues-become-w7s-queues">Work Queues Become W7S Queues<a href="https://w7s.io/docs/blog/replacing-nats-with-w7s-components/#work-queues-become-w7s-queues" class="hash-link" aria-label="Direct link to Work Queues Become W7S Queues" title="Direct link to Work Queues Become W7S Queues" translate="no">​</a></h2>
<p>NATS queue groups are useful when work should be processed asynchronously by one of several workers. In W7S, the queue is owned by a target repository, and producers send JSON messages through the W7S queue binding.</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"queues"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"jobs"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"consumer"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/_w7s/queues/jobs"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>A producer sends to the target repo and queue:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/index.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">type</span><span class="token plain"> </span><span class="token class-name">Env</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token constant" style="color:rgb(189, 147, 249)">W7S_QUEUE</span><span class="token operator">:</span><span class="token plain"> Fetcher</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token constant" style="color:rgb(189, 147, 249)">W7S_QUEUE_TOKEN</span><span class="token operator">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">string</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:rgb(80, 250, 123)">enqueueJob</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">env</span><span class="token operator">:</span><span class="token plain"> Env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> payload</span><span class="token operator">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">unknown</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">=&gt;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">W7S_QUEUE</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token string" style="color:rgb(255, 121, 198)">"https://w7s.internal/api/v1/queues/acme/worker/jobs"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      method</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"POST"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      headers</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        authorization</span><span class="token operator">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token template-string string" style="color:rgb(255, 121, 198)">Bearer </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">${</span><span class="token template-string interpolation">env</span><span class="token template-string interpolation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token template-string interpolation constant" style="color:rgb(189, 147, 249)">W7S_QUEUE_TOKEN</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token string-property property">"content-type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"application/json"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      body</span><span class="token operator">:</span><span class="token plain"> </span><span class="token constant" style="color:rgb(189, 147, 249)">JSON</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">stringify</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">payload</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>The consumer receives batches at its configured path:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/index.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">async</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token operator">:</span><span class="token plain"> Request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> url </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name constant" style="color:rgb(189, 147, 249)">URL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">pathname </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/_w7s/queues/jobs"</span><span class="token plain"> </span><span class="token operator">&amp;&amp;</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">method </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"POST"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> batch </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">for</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> message </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">of</span><span class="token plain"> batch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">messages</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token builtin" style="color:rgb(189, 147, 249)">console</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">log</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"processing"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> message</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> message</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">body</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">ok</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> processed</span><span class="token operator">:</span><span class="token plain"> batch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">messages</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">length</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Not found"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">status</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">404</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>Use queues when the caller does not need an immediate result and retry behavior is more important than latency.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="pubsub-becomes-explicit-event-fanout">Pub/Sub Becomes Explicit Event Fanout<a href="https://w7s.io/docs/blog/replacing-nats-with-w7s-components/#pubsub-becomes-explicit-event-fanout" class="hash-link" aria-label="Direct link to Pub/Sub Becomes Explicit Event Fanout" title="Direct link to Pub/Sub Becomes Explicit Event Fanout" translate="no">​</a></h2>
<p>NATS subjects are attractive because publishers do not need to know every subscriber. That flexibility is valuable, but it also means the broker becomes the place where important topology lives.</p>
<p>W7S can take a more explicit approach: add an event router backend that owns subscription metadata and forwards each event to target queues.</p>
<p>One possible manifest shape:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"events"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"publish"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"orders.created"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"orders.cancelled"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"subscribe"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"subject"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"orders.created"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"queue"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"order-events"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"queues"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"order-events"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The first version does not need to introduce new infrastructure. Internally, it can be built from:</p>
<ul>
<li class="">deployment metadata in W7S records;</li>
<li class="">a <code>W7S_EVENTS</code> service binding, similar to <code>W7S_QUEUE</code>;</li>
<li class="">Cloudflare Queues for delivery;</li>
<li class="">existing queue delivery and usage accounting paths.</li>
</ul>
<p>The event router would:</p>
<ol>
<li class="">authenticate the publisher with a W7S-issued token;</li>
<li class="">validate that the publisher is allowed to publish the subject;</li>
<li class="">look up subscriptions for the same environment;</li>
<li class="">enqueue one delivery message per subscriber queue;</li>
<li class="">record <code>event.publish</code> and <code>event.delivery</code> usage.</li>
</ol>
<p>This is less dynamic than NATS wildcard subjects, but it is easier to explain from a repository. The repo declares what it emits and what it consumes.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="durable-streams-become-db-kv-r2-and-queues">Durable Streams Become DB, KV, R2, and Queues<a href="https://w7s.io/docs/blog/replacing-nats-with-w7s-components/#durable-streams-become-db-kv-r2-and-queues" class="hash-link" aria-label="Direct link to Durable Streams Become DB, KV, R2, and Queues" title="Direct link to Durable Streams Become DB, KV, R2, and Queues" translate="no">​</a></h2>
<p>JetStream is one of the places where "replace NATS" needs more care. Persistence is not one feature; it is several different product needs that can look similar at first.</p>
<p>For W7S, split the need by data shape:</p>
<table><thead><tr><th>Need</th><th>W7S component</th></tr></thead><tbody><tr><td>Audit trail with query filters</td><td>Serverless DB</td></tr><tr><td>Latest state by key</td><td>KV</td></tr><tr><td>Large event payloads</td><td>R2</td></tr><tr><td>Async delivery after writing event</td><td>Queue</td></tr><tr><td>Multi-step replay or repair</td><td>Workflow</td></tr></tbody></table>
<p>A common W7S-native event store can look like this:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"bindings"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"d1"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"binding"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"DB"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"migrations"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"migrations"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"r2"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"EVENT_BODIES"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"queues"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"event-delivery"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The publisher writes a durable event record, stores large bodies in R2 when needed, and then sends a queue message that references the stored event id.</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/events.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">persistAndDispatch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">env</span><span class="token operator">:</span><span class="token plain"> Env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> event</span><span class="token operator">:</span><span class="token plain"> AppEvent</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> id </span><span class="token operator">=</span><span class="token plain"> crypto</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">randomUUID</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> body </span><span class="token operator">=</span><span class="token plain"> </span><span class="token constant" style="color:rgb(189, 147, 249)">JSON</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">stringify</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">event</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">DB</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">prepare</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token string" style="color:rgb(255, 121, 198)">"insert into events (id, subject, body, created_at) values (?, ?, ?, ?)"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">bind</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> event</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">subject</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> body</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">Date</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">toISOString</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">run</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">W7S_QUEUE</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token string" style="color:rgb(255, 121, 198)">"https://w7s.internal/api/v1/queues/acme/events/event-delivery"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      method</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"POST"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      headers</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        authorization</span><span class="token operator">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token template-string string" style="color:rgb(255, 121, 198)">Bearer </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">${</span><span class="token template-string interpolation">env</span><span class="token template-string interpolation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token template-string interpolation constant" style="color:rgb(189, 147, 249)">W7S_QUEUE_TOKEN</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token string-property property">"content-type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"application/json"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      body</span><span class="token operator">:</span><span class="token plain"> </span><span class="token constant" style="color:rgb(189, 147, 249)">JSON</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">stringify</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">eventId</span><span class="token operator">:</span><span class="token plain"> id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>That does not reproduce every JetStream feature. It does give the application explicit storage, query, replay, and delivery behavior using W7S resources that are already scoped per repo and environment.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="schedules-replace-scheduled-publishes">Schedules Replace Scheduled Publishes<a href="https://w7s.io/docs/blog/replacing-nats-with-w7s-components/#schedules-replace-scheduled-publishes" class="hash-link" aria-label="Direct link to Schedules Replace Scheduled Publishes" title="Direct link to Schedules Replace Scheduled Publishes" translate="no">​</a></h2>
<p>If a NATS subject is mostly fed by cron jobs, W7S schedules are the cleaner primitive. The app declares a schedule and a backend path.</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"schedules"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"cron"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"*/5 * * * *"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"path"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/_w7s/schedules/sync"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The scheduled handler can write an event, call RPC, or enqueue work:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/index.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">async</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token operator">:</span><span class="token plain"> Request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> env</span><span class="token operator">:</span><span class="token plain"> Env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> url </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name constant" style="color:rgb(189, 147, 249)">URL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">pathname </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/_w7s/schedules/sync"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">enqueueSync</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">ok</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Not found"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">status</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">404</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>This keeps time-based behavior in the target repo instead of hiding it in a broker-side convention.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="workflows-replace-durable-orchestration">Workflows Replace Durable Orchestration<a href="https://w7s.io/docs/blog/replacing-nats-with-w7s-components/#workflows-replace-durable-orchestration" class="hash-link" aria-label="Direct link to Workflows Replace Durable Orchestration" title="Direct link to Workflows Replace Durable Orchestration" translate="no">​</a></h2>
<p>Some NATS usage is not really messaging. It is orchestration:</p>
<ul>
<li class="">start task A;</li>
<li class="">wait for task B;</li>
<li class="">retry task C;</li>
<li class="">write status;</li>
<li class="">notify another app.</li>
</ul>
<p>W7S Workflows are a better fit for that shape than raw pub/sub. They give a named process boundary, a start API, and durable delivery through W7S-managed workflow dispatch.</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"workflows"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"name"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"checkout"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"path"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/_w7s/workflows/checkout"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Callers start the workflow through <code>W7S_WORKFLOW</code>; the target receives the workflow payload at the declared path. Use this for user-visible processes where "message was published" is not enough. The product needs a durable process state.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="local-development-without-a-broker">Local Development Without a Broker<a href="https://w7s.io/docs/blog/replacing-nats-with-w7s-components/#local-development-without-a-broker" class="hash-link" aria-label="Direct link to Local Development Without a Broker" title="Direct link to Local Development Without a Broker" translate="no">​</a></h2>
<p>One reason teams like NATS locally is that it gives every service the same communication surface in development. W7S can cover most of that with <code>w7s-local</code> and small HTTP fallbacks.</p>
<p>For RPC, write helpers with two paths:</p>
<ul>
<li class="">hosted W7S uses <code>env.W7S_RPC</code>;</li>
<li class="">local development calls the target repo's <code>w7s-local</code> URL.</li>
</ul>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/rpc.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">callAuth</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">env</span><span class="token operator">:</span><span class="token plain"> Env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> cookie</span><span class="token operator">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">string</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">W7S_RPC</span><span class="token plain"> </span><span class="token operator">&amp;&amp;</span><span class="token plain"> env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">W7S_RPC_TOKEN</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">W7S_RPC</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token string" style="color:rgb(255, 121, 198)">"https://w7s.internal/api/v1/rpc/acme/auth/session"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        headers</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          authorization</span><span class="token operator">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token template-string string" style="color:rgb(255, 121, 198)">Bearer </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">${</span><span class="token template-string interpolation">env</span><span class="token template-string interpolation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token template-string interpolation constant" style="color:rgb(189, 147, 249)">W7S_RPC_TOKEN</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          cookie</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">LOCAL_AUTH_URL</span><span class="token plain"> </span><span class="token operator">??</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"http://127.0.0.1:8788/auth/session"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      headers</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        cookie</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token string-property property">"x-w7s-rpc"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"1"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token string-property property">"x-w7s-rpc-caller-repository"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"acme/app"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>This keeps local development simple without asking every developer to run a broker just to test two repositories talking to each other.</p>
<p>For a runnable version of the event-router pattern, see
<a href="https://github.com/w7s-io/docs/tree/main/examples/w7s-local-native-events" target="_blank" rel="noopener noreferrer" class=""><code>examples/w7s-local-native-events</code></a>.
It starts three local W7S repos with <code>w7s-local</code>: an order API, an event router, and a queue consumer.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-w7s-still-does-not-replace">What W7S Still Does Not Replace<a href="https://w7s.io/docs/blog/replacing-nats-with-w7s-components/#what-w7s-still-does-not-replace" class="hash-link" aria-label="Direct link to What W7S Still Does Not Replace" title="Direct link to What W7S Still Does Not Replace" translate="no">​</a></h2>
<p>There are real NATS capabilities that W7S should not pretend to have today:</p>
<ul>
<li class="">dynamic wildcard subject subscriptions;</li>
<li class="">high-volume many-subscriber streaming;</li>
<li class="">long-lived service subscriptions inside always-on processes;</li>
<li class="">mature broker clustering and leaf-node topologies;</li>
<li class="">JetStream consumer semantics such as replay policies and ordered consumers;</li>
<li class="">NATS account/operator tooling.</li>
</ul>
<p>If an application depends on those features directly, NATS may still be the right dependency. W7S compatibility can still be useful there, but it should be explicit external-service compatibility, not a hidden broker inside W7S.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="a-w7s-native-events-layer">A W7S-Native Events Layer<a href="https://w7s.io/docs/blog/replacing-nats-with-w7s-components/#a-w7s-native-events-layer" class="hash-link" aria-label="Direct link to A W7S-Native Events Layer" title="Direct link to A W7S-Native Events Layer" translate="no">​</a></h2>
<p>The most useful future feature is not "run NATS inside W7S." It is a W7S-native Events layer built on the components already present.</p>
<p>The developer-facing API could be small:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">await</span><span class="token plain"> env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">W7S_EVENTS</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"https://w7s.internal/api/v1/events/acme/orders"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  method</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"POST"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  headers</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    authorization</span><span class="token operator">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token template-string string" style="color:rgb(255, 121, 198)">Bearer </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">${</span><span class="token template-string interpolation">env</span><span class="token template-string interpolation punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token template-string interpolation constant" style="color:rgb(189, 147, 249)">W7S_EVENTS_TOKEN</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token template-string template-punctuation string" style="color:rgb(255, 121, 198)">`</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token string-property property">"content-type"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"application/json"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  body</span><span class="token operator">:</span><span class="token plain"> </span><span class="token constant" style="color:rgb(189, 147, 249)">JSON</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">stringify</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    subject</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"orders.created"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    data</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      orderId</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"ord_123"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>The manifest could stay explicit:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"events"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"publish"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"orders.created"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"subscribe"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"subject"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"orders.created"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"queue"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"orders"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"queues"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"orders"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Under the hood, W7S can reuse queue provisioning, queue delivery, deployment metadata, per-repo authorization, branch environment isolation, and usage accounting. That keeps the platform coherent.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="recommendation">Recommendation<a href="https://w7s.io/docs/blog/replacing-nats-with-w7s-components/#recommendation" class="hash-link" aria-label="Direct link to Recommendation" title="Direct link to Recommendation" translate="no">​</a></h2>
<p>Do not add NATS as a required W7S dependency.</p>
<p>Use existing W7S primitives first:</p>
<ul>
<li class="">RPC for synchronous service calls;</li>
<li class="">Queues for asynchronous work;</li>
<li class="">Schedules for time-based producers;</li>
<li class="">Workflows for durable orchestration;</li>
<li class="">DB, KV, and R2 for persistence;</li>
<li class=""><code>w7s-local</code> for multi-repo development.</li>
</ul>
<p>Then add a small Events layer when the missing abstraction becomes painful. That gives W7S most of the product ergonomics people reach for NATS to get, without taking on a separate broker as part of the default platform.</p>]]></content:encoded>
            <category>architecture</category>
            <category>queues</category>
            <category>rpc</category>
            <category>workflows</category>
            <category>events</category>
        </item>
        <item>
            <title><![CDATA[Replacing Vercel and Netlify With W7S]]></title>
            <link>https://w7s.io/docs/blog/replacing-vercel-and-netlify-with-w7s/</link>
            <guid>https://w7s.io/docs/blog/replacing-vercel-and-netlify-with-w7s/</guid>
            <pubDate>Sun, 31 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[When a frontend platform starts becoming an app platform, W7S can move deploys, backends, storage, previews, and domains back into the repository.]]></description>
            <content:encoded><![CDATA[<p>Vercel and Netlify made frontend deployment feel simple: connect a repository, let the platform build it, get a URL, and add serverless functions when the site needs backend behavior.</p>
<p>That model is still useful. But as a project grows, the platform often becomes more than a static host. It starts owning deploy triggers, preview behavior, environment settings, backend functions, edge logic, storage integrations, domain configuration, observability, and team permissions.</p>
<p>W7S takes a different path:</p>
<blockquote>
<p>Keep the deployment workflow in GitHub, keep the runtime contract in the repository, and let the platform provide app bindings directly.</p>
</blockquote>
<p>This article maps the common Vercel and Netlify product shape onto W7S components.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-short-mapping">The Short Mapping<a href="https://w7s.io/docs/blog/replacing-vercel-and-netlify-with-w7s/#the-short-mapping" class="hash-link" aria-label="Direct link to The Short Mapping" title="Direct link to The Short Mapping" translate="no">​</a></h2>
<table><thead><tr><th>Vercel / Netlify concept</th><th>W7S replacement</th><th>Best fit</th></tr></thead><tbody><tr><td>Connected project</td><td>GitHub repository plus W7S action</td><td>Deploys controlled from CI</td></tr><tr><td>Production deploy</td><td><code>main</code> or <code>master</code> workflow deploy</td><td>Stable production environment</td></tr><tr><td>Preview deploy</td><td>Branch environment</td><td>Reviewable URLs and isolated bindings</td></tr><tr><td>Serverless functions</td><td>Native backend</td><td>One backend entrypoint with normal routing</td></tr><tr><td>Edge functions</td><td>Native backend on the W7S runtime</td><td>Small request handlers near the app</td></tr><tr><td>Environment variables</td><td><code>w7s.json</code> vars and secrets</td><td>Repo-declared runtime contract</td></tr><tr><td>Storage integrations</td><td>DB, KV, FS, queues, workflows</td><td>Managed resources scoped by repo and environment</td></tr><tr><td>Custom domains</td><td><code>CNAME</code> file plus DNS authorization</td><td>Domain claims visible in the repo</td></tr><tr><td>Build settings</td><td>GitHub Actions workflow</td><td>Install, build, and package steps in code review</td></tr><tr><td>Platform dashboard</td><td>GitHub workflow plus W7S deploy API</td><td>Auditable deploy path</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="source-backed-comparison-points">Source-Backed Comparison Points<a href="https://w7s.io/docs/blog/replacing-vercel-and-netlify-with-w7s/#source-backed-comparison-points" class="hash-link" aria-label="Direct link to Source-Backed Comparison Points" title="Direct link to Source-Backed Comparison Points" translate="no">​</a></h2>
<p>Vercel and Netlify are credible references precisely because they made frontend deployment easy. Vercel documents <a href="https://vercel.com/docs/deployments" target="_blank" rel="noopener noreferrer" class="">deployments</a> and <a href="https://vercel.com/docs/functions" target="_blank" rel="noopener noreferrer" class="">functions</a> as core pieces of its app platform, while Netlify documents <a href="https://docs.netlify.com/deploy/deploy-types/deploy-previews/" target="_blank" rel="noopener noreferrer" class="">deploy previews</a>, <a href="https://docs.netlify.com/build/functions/overview/" target="_blank" rel="noopener noreferrer" class="">functions</a>, and product features such as <a href="https://docs.netlify.com/manage/forms/setup/" target="_blank" rel="noopener noreferrer" class="">forms</a>. Those are strong defaults for teams that want the platform dashboard to own the release and app experience.</p>
<p>W7S makes a different architectural bet: the repository should own the release path. <a class="" href="https://w7s.io/docs/deploy-from-github/">Deploying from GitHub</a> makes GitHub Actions the control plane, so build commands, output directories, permissions, and deploy triggers are reviewable with the code. That does not remove the need for a platform; it moves the platform interaction into an auditable workflow.</p>
<p>The backend story is also different. Vercel and Netlify expose functions as platform features attached to a project. W7S uses <a class="" href="https://w7s.io/docs/project-layouts/">project layouts</a> to package static output and a native backend entrypoint from the same repository, then uses <a class="" href="https://w7s.io/docs/urls-and-routing/">URLs and routing</a> to derive public routes from the GitHub owner and repo. For apps that want one backend handler with normal routing, that can be simpler than scattering logic across function files and dashboard settings.</p>
<p>W7S becomes especially attractive once the app needs state. <a class="" href="https://w7s.io/docs/storage-bindings/">Storage bindings</a>, <a class="" href="https://w7s.io/docs/backend-queues/">backend queues</a>, and <a class="" href="https://w7s.io/docs/backend-workflows/">backend workflows</a> let the repository describe databases, caches, files, async work, and durable processes in the same deployment contract. That is the part that makes W7S a real alternative rather than just another static host.</p>
<p>The pricing and operations argument should be grounded in workflow, not slogans. W7S <a class="" href="https://w7s.io/docs/usage-accounting/">usage accounting</a> focuses on app-level consumption and keeps the control plane self-hostable for teams that need ownership. Vercel and Netlify remain good choices when their integrated dashboards and ecosystem are the goal; W7S is better when the team wants GitHub-native deploys, portable conventions, and fewer hidden platform settings.</p>
<p>W7S is not a drop-in clone of either platform. It is a narrower app platform for repositories that can build in GitHub Actions and run as static assets plus JavaScript/TypeScript native backends.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="deployment-moves-back-to-github-actions">Deployment Moves Back to GitHub Actions<a href="https://w7s.io/docs/blog/replacing-vercel-and-netlify-with-w7s/#deployment-moves-back-to-github-actions" class="hash-link" aria-label="Direct link to Deployment Moves Back to GitHub Actions" title="Direct link to Deployment Moves Back to GitHub Actions" translate="no">​</a></h2>
<p>On Vercel and Netlify, the usual path starts by connecting a Git repository to a hosted project. That project watches pushes, runs a build, and publishes the result.</p>
<p>W7S keeps the same basic convenience, but changes the owner of the release process. The workflow file is the deploy control plane:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">.github/workflows/deploy.yml</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Deploy</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">on</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">push</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">branches</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">main</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">jobs</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">deploy</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">runs-on</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> ubuntu</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">latest</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">permissions</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">contents</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> read</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">id-token</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> write</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">steps</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> actions/checkout@v5</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> actions/setup</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">node@v6</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token key atrule">node-version</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">22</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> npm ci</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> npm run build</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> w7s</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">io/w7s</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">cloud@v1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token key atrule">token</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> github.token </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token key atrule">production-branch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> main</span><br></div></code></pre></div></div>
<p>That makes deploy behavior reviewable with the app. If a branch changes the build command, environment inputs, output directory, or deploy permissions, reviewers see it in the pull request.</p>
<p>The tradeoff is direct: W7S gives up some dashboard-led convenience in exchange for a release path that is plain repository state.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="static-sites-stay-static-until-they-need-more">Static Sites Stay Static Until They Need More<a href="https://w7s.io/docs/blog/replacing-vercel-and-netlify-with-w7s/#static-sites-stay-static-until-they-need-more" class="hash-link" aria-label="Direct link to Static Sites Stay Static Until They Need More" title="Direct link to Static Sites Stay Static Until They Need More" translate="no">​</a></h2>
<p>For static frontends, W7S has the same core shape as a frontend platform:</p>
<ol>
<li class="">GitHub Actions installs dependencies.</li>
<li class="">The workflow builds the frontend.</li>
<li class="">W7S publishes the generated output.</li>
<li class="">The app gets a stable URL derived from the GitHub owner and repository.</li>
</ol>
<p>Common output directories such as <code>dist/</code>, <code>build/</code>, and <code>frontend/dist/</code> can be deployed directly.</p>
<p>The useful difference appears later. If the site needs a backend route, queue, schedule, workflow, serverless database, file bucket, key-value namespace, or internal service call, the repository can declare those pieces without moving to a different platform model.</p>
<p>Static does not become a dead end.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="functions-become-one-native-backend">Functions Become One Native Backend<a href="https://w7s.io/docs/blog/replacing-vercel-and-netlify-with-w7s/#functions-become-one-native-backend" class="hash-link" aria-label="Direct link to Functions Become One Native Backend" title="Direct link to Functions Become One Native Backend" translate="no">​</a></h2>
<p>Serverless function platforms often organize backend code as many files mapped to routes. That is convenient for small endpoints. It can become repetitive once the app needs shared middleware, auth, validation, logging, queue helpers, database access, and error handling.</p>
<p>W7S uses a native backend entrypoint:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/index.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">async</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token operator">:</span><span class="token plain"> Request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> env</span><span class="token operator">:</span><span class="token plain"> Env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> ctx</span><span class="token operator">:</span><span class="token plain"> ExecutionContext</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> url </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name constant" style="color:rgb(189, 147, 249)">URL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">pathname </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/api/health"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">ok</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">pathname </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/api/orders"</span><span class="token plain"> </span><span class="token operator">&amp;&amp;</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">method </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"POST"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">createOrder</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Not found"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">status</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">404</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>You can use a router such as Hono, but you do not have to. The backend is a small service that receives requests and decides routing in code.</p>
<p>That works well when the backend is more than a handful of independent functions but still does not need a container or long-running process.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="storage-is-declared-with-the-app">Storage Is Declared With the App<a href="https://w7s.io/docs/blog/replacing-vercel-and-netlify-with-w7s/#storage-is-declared-with-the-app" class="hash-link" aria-label="Direct link to Storage Is Declared With the App" title="Direct link to Storage Is Declared With the App" translate="no">​</a></h2>
<p>Frontend platforms often start with functions and then add storage through integrations, plugins, external databases, or provider-specific setup.</p>
<p>W7S puts the app's platform needs in <code>w7s.json</code>:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"bindings"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"db"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"binding"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"DB"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"migrations"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"migrations"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"kv"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"CACHE"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"fs"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"FILES"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"queues"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"jobs"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"schedules"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"cron"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"*/15 * * * *"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"path"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/_w7s/schedules/sync"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The backend reads the bindings from <code>env</code>:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/index.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">type</span><span class="token plain"> </span><span class="token class-name">Env</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token constant" style="color:rgb(189, 147, 249)">DB</span><span class="token operator">:</span><span class="token plain"> D1Database</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token constant" style="color:rgb(189, 147, 249)">CACHE</span><span class="token operator">:</span><span class="token plain"> KVNamespace</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token constant" style="color:rgb(189, 147, 249)">FILES</span><span class="token operator">:</span><span class="token plain"> R2Bucket</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token constant" style="color:rgb(189, 147, 249)">W7S_QUEUE</span><span class="token operator">:</span><span class="token plain"> Fetcher</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token constant" style="color:rgb(189, 147, 249)">W7S_QUEUE_TOKEN</span><span class="token operator">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(189, 147, 249)">string</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>This keeps the runtime contract visible in the repository. The app does not need a separate dashboard checklist to explain which storage resources must exist.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="preview-deploys-become-branch-environments">Preview Deploys Become Branch Environments<a href="https://w7s.io/docs/blog/replacing-vercel-and-netlify-with-w7s/#preview-deploys-become-branch-environments" class="hash-link" aria-label="Direct link to Preview Deploys Become Branch Environments" title="Direct link to Preview Deploys Become Branch Environments" translate="no">​</a></h2>
<p>Vercel and Netlify popularized preview URLs because reviewing a deployed branch is better than reviewing screenshots.</p>
<p>W7S keeps that idea, but ties it to branch environments:</p>
<ul>
<li class=""><code>main</code> and <code>master</code> deploy to <code>production</code>;</li>
<li class="">other branches deploy to branch-derived environment names;</li>
<li class="">URLs include the environment prefix;</li>
<li class="">managed resources can be scoped by repository and environment.</li>
</ul>
<p>That means a branch can test more than a static UI. It can test backend routes, storage declarations, queues, schedules, and workflows without accidentally sharing production bindings.</p>
<p>This is the more important preview feature. The URL is useful. The isolated runtime contract is the real value.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="custom-domains-stay-in-the-repository">Custom Domains Stay in the Repository<a href="https://w7s.io/docs/blog/replacing-vercel-and-netlify-with-w7s/#custom-domains-stay-in-the-repository" class="hash-link" aria-label="Direct link to Custom Domains Stay in the Repository" title="Direct link to Custom Domains Stay in the Repository" translate="no">​</a></h2>
<p>On many platforms, custom domains are project settings. That is convenient until the domain becomes part of the app contract and no one remembers where it is configured.</p>
<p>W7S lets the repository declare hostnames with a <code>CNAME</code> file:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">CNAME</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token plain">www.example.com</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">app.example.com</span><br></div></code></pre></div></div>
<p>DNS still decides ownership. A domain owner can point records at W7S and use <code>_w7s.&lt;zone&gt;</code> TXT authorization to allow a whole GitHub owner or one exact <code>owner/repo</code>.</p>
<p>The result is a clean split:</p>
<ul>
<li class="">the repo requests the hostname;</li>
<li class="">DNS authorizes the hostname;</li>
<li class="">W7S maps the deploy.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="when-vercel-or-netlify-still-fit-better">When Vercel or Netlify Still Fit Better<a href="https://w7s.io/docs/blog/replacing-vercel-and-netlify-with-w7s/#when-vercel-or-netlify-still-fit-better" class="hash-link" aria-label="Direct link to When Vercel or Netlify Still Fit Better" title="Direct link to When Vercel or Netlify Still Fit Better" translate="no">​</a></h2>
<p>Keep Vercel or Netlify when the application depends on their exact product surface:</p>
<ul>
<li class="">framework-specific behavior that is tightly integrated with the platform;</li>
<li class="">dashboard-managed team workflows and project settings;</li>
<li class="">mature preview comment workflows and integrations;</li>
<li class="">Netlify forms, plugins, or identity features;</li>
<li class="">Vercel-specific Next.js platform capabilities;</li>
<li class="">platform-specific analytics or observability;</li>
<li class="">existing organization policy around those services.</li>
</ul>
<p>W7S is not trying to copy every feature. It is trying to make the smaller path clearer for apps that fit its runtime.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="recommendation">Recommendation<a href="https://w7s.io/docs/blog/replacing-vercel-and-netlify-with-w7s/#recommendation" class="hash-link" aria-label="Direct link to Recommendation" title="Direct link to Recommendation" translate="no">​</a></h2>
<p>Use W7S when the app can be described as:</p>
<ul>
<li class="">static assets built in GitHub Actions;</li>
<li class="">a JavaScript/TypeScript native backend;</li>
<li class="">repo-declared storage, queues, schedules, workflows, vars, and secrets;</li>
<li class="">branch environments for previews;</li>
<li class="">GitHub as the release control plane.</li>
</ul>
<p>Use Vercel or Netlify when their managed frontend platform and ecosystem integrations are the product you want.</p>
<p>The practical line is ownership. If you want deploy behavior and runtime shape to live in the repository, W7S is the cleaner model. If you want a hosted frontend dashboard to own that workflow, stay with the frontend platform.</p>]]></content:encoded>
            <category>platforms</category>
            <category>alternatives</category>
            <category>github-actions</category>
            <category>backends</category>
        </item>
        <item>
            <title><![CDATA[Vercel Competitors]]></title>
            <link>https://w7s.io/docs/blog/vercel-competitors/</link>
            <guid>https://w7s.io/docs/blog/vercel-competitors/</guid>
            <pubDate>Sun, 31 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A practical comparison of Vercel alternatives, including Netlify, Cloudflare Pages, Render, Railway, Fly.io, GitHub Pages, AWS Amplify, Firebase, Supabase, and why W7S is better for GitHub-native apps.]]></description>
            <content:encoded><![CDATA[<p>The best Vercel competitor depends on what you want to replace.</p>
<p>If you want a similar frontend platform, Netlify and Cloudflare Pages are the obvious comparisons. If you want to run containers or long-lived services, Render, Railway, and Fly.io are closer. If you only need static hosting, GitHub Pages may be enough. If you want a larger backend product, AWS Amplify, Firebase, and Supabase enter the conversation.</p>
<p>W7S competes from a different angle:</p>
<blockquote>
<p>W7S is the Vercel competitor for teams that want GitHub, not a hosted dashboard, to be the deployment control plane.</p>
</blockquote>
<p>That makes W7S better when the repository should own the deploy workflow, runtime contract, app URL, branch environments, backend bindings, and path to self-hosting.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-short-list">The Short List<a href="https://w7s.io/docs/blog/vercel-competitors/#the-short-list" class="hash-link" aria-label="Direct link to The Short List" title="Direct link to The Short List" translate="no">​</a></h2>
<table><thead><tr><th>Competitor</th><th>Best fit</th><th>Tradeoff</th></tr></thead><tbody><tr><td>W7S</td><td>GitHub-native apps with static assets, native backends, storage, queues, schedules, and workflows</td><td>Narrower runtime model than general container platforms</td></tr><tr><td>Netlify</td><td>Frontend teams that like deploy previews, functions, forms, and a mature web dashboard</td><td>Project behavior still lives partly in a hosted product</td></tr><tr><td>Cloudflare Pages</td><td>Frontend apps that want Cloudflare's edge network and Pages Functions</td><td>More primitive wiring when the app grows beyond Pages</td></tr><tr><td>Render</td><td>Web services, background workers, cron jobs, and managed services</td><td>More process-oriented than many small apps need</td></tr><tr><td>Railway</td><td>Fast project setup for services and databases</td><td>Dashboard/project model remains central</td></tr><tr><td>Fly.io</td><td>Apps that need machines, regions, and lower-level runtime control</td><td>You operate closer to infrastructure</td></tr><tr><td>GitHub Pages</td><td>Pure static sites</td><td>No backend, storage, queues, or app runtime</td></tr><tr><td>AWS Amplify</td><td>Teams already deep in AWS app services</td><td>AWS complexity and account model come with it</td></tr><tr><td>Firebase</td><td>Realtime/mobile/web apps using Google's backend stack</td><td>Strong ecosystem, less repo-owned deployment shape</td></tr><tr><td>Supabase</td><td>Postgres-centered apps with auth and realtime needs</td><td>Excellent backend product, not a deploy-platform replacement by itself</td></tr></tbody></table>
<p>Vercel is still a strong product, especially for Next.js-centric teams that want Vercel's hosted workflow and framework integration. The question is whether that is the workflow you want.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="source-backed-comparison-points">Source-Backed Comparison Points<a href="https://w7s.io/docs/blog/vercel-competitors/#source-backed-comparison-points" class="hash-link" aria-label="Direct link to Source-Backed Comparison Points" title="Direct link to Source-Backed Comparison Points" translate="no">​</a></h2>
<p>Vercel's platform position is documented through its own material on <a href="https://vercel.com/docs/deployments" target="_blank" rel="noopener noreferrer" class="">deployments</a>, <a href="https://vercel.com/docs/functions" target="_blank" rel="noopener noreferrer" class="">Vercel Functions</a>, and <a href="https://vercel.com/pricing" target="_blank" rel="noopener noreferrer" class="">pricing</a>. Those sources show that a Vercel comparison is not only about hosting static files. It is about CI/CD, frontend infrastructure, compute, storage, and product-level controls.</p>
<p>The W7S comparison is grounded in the W7S docs for <a class="" href="https://w7s.io/docs/deploy-from-github/">deploying from GitHub</a>, <a class="" href="https://w7s.io/docs/project-layouts/">project layouts</a>, <a class="" href="https://w7s.io/docs/urls-and-routing/">URLs and routing</a>, and <a class="" href="https://w7s.io/docs/storage-bindings/">storage bindings</a>. Those pages describe the repository-first deployment model, static and native backend shapes, owner/repo-derived URLs, and declared runtime resources.</p>
<p>Netlify is the closest frontend-platform comparison because its docs cover <a href="https://docs.netlify.com/deploy/deploy-types/deploy-previews/" target="_blank" rel="noopener noreferrer" class="">Deploy Previews</a>, <a href="https://docs.netlify.com/build/functions/overview/" target="_blank" rel="noopener noreferrer" class="">Functions</a>, <a href="https://docs.netlify.com/build/edge-functions/overview/" target="_blank" rel="noopener noreferrer" class="">Edge Functions</a>, <a href="https://docs.netlify.com/manage/forms/setup/" target="_blank" rel="noopener noreferrer" class="">Forms</a>, and <a href="https://docs.netlify.com/build/functions/scheduled-functions/" target="_blank" rel="noopener noreferrer" class="">Scheduled Functions</a>. That backs the article's claim that Netlify is a frontend workflow alternative, while W7S is a repository-owned app platform alternative.</p>
<p>Process platforms are a different category. Render documents <a href="https://render.com/docs/web-services" target="_blank" rel="noopener noreferrer" class="">web services</a>, <a href="https://render.com/docs/background-workers" target="_blank" rel="noopener noreferrer" class="">background workers</a>, and <a href="https://render.com/docs/cronjobs" target="_blank" rel="noopener noreferrer" class="">cron jobs</a>; Railway describes its <a href="https://docs.railway.com/platform" target="_blank" rel="noopener noreferrer" class="">platform model</a>; Fly.io documents <a href="https://fly.io/docs/machines/" target="_blank" rel="noopener noreferrer" class="">Machines</a>; Google documents <a href="https://docs.cloud.google.com/run/docs" target="_blank" rel="noopener noreferrer" class="">Cloud Run</a>; and AWS documents <a href="https://docs.aws.amazon.com/apprunner/latest/dg/what-is-apprunner.html" target="_blank" rel="noopener noreferrer" class="">App Runner</a>. Those sources back the distinction between process/container hosting and W7S's narrower app-runtime model.</p>
<p>The argument for W7S as a real alternative depends on backend capability, not only deploy ergonomics. W7S documents <a class="" href="https://w7s.io/docs/backend-queues/">Backend Queues</a>, <a class="" href="https://w7s.io/docs/backend-schedules/">Backend Schedules</a>, <a class="" href="https://w7s.io/docs/backend-workflows/">Backend Workflows</a>, <a class="" href="https://w7s.io/docs/backend-rpc/">Backend RPC</a>, <a class="" href="https://w7s.io/docs/usage-accounting/">Usage Accounting</a>, and <a class="" href="https://w7s.io/docs/self-host/">Self Hosting</a>, which is the set of primitives this article relies on when describing W7S as more than static hosting.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-vercel-is-really-selling">What Vercel Is Really Selling<a href="https://w7s.io/docs/blog/vercel-competitors/#what-vercel-is-really-selling" class="hash-link" aria-label="Direct link to What Vercel Is Really Selling" title="Direct link to What Vercel Is Really Selling" translate="no">​</a></h2>
<p>Vercel is not just static hosting. A typical Vercel project gives you:</p>
<ul>
<li class="">Git-connected deployments;</li>
<li class="">preview deployments for branches and pull requests;</li>
<li class="">production deploys from a production branch;</li>
<li class="">framework-aware builds;</li>
<li class="">serverless and edge runtime features;</li>
<li class="">project environment variables;</li>
<li class="">custom domains;</li>
<li class="">dashboard-based project management;</li>
<li class="">integrations and marketplace features.</li>
</ul>
<p>That is a good default for many frontend teams. It also means the deployment product becomes a second source of truth beside the repository.</p>
<p>W7S starts from the opposite assumption: the repository and GitHub Actions workflow should be enough to understand how the app ships.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-w7s-is-better-for-github-native-apps">Why W7S Is Better for GitHub-Native Apps<a href="https://w7s.io/docs/blog/vercel-competitors/#why-w7s-is-better-for-github-native-apps" class="hash-link" aria-label="Direct link to Why W7S Is Better for GitHub-Native Apps" title="Direct link to Why W7S Is Better for GitHub-Native Apps" translate="no">​</a></h2>
<p>W7S is better than Vercel when these are your priorities:</p>
<ul>
<li class="">deploys should be defined in <code>.github/workflows/deploy.yml</code>;</li>
<li class="">the deploy token should be the GitHub token;</li>
<li class="">app URLs should be derived from GitHub owner and repository;</li>
<li class="">branch environments should be predictable and repo-scoped;</li>
<li class="">static assets and backend routes should deploy together;</li>
<li class="">storage, queues, schedules, workflows, vars, and secrets should be declared in the repo;</li>
<li class="">service-to-service calls should use repository identity;</li>
<li class="">custom domains should be visible in a <code>CNAME</code> file;</li>
<li class="">the platform should have an open-source/self-hostable path.</li>
</ul>
<p>That is the difference. Vercel makes deployment feel easy by centralizing more behavior in Vercel. W7S makes deployment easier to audit by keeping the behavior in GitHub and the repository.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="w7s-vs-vercel">W7S vs Vercel<a href="https://w7s.io/docs/blog/vercel-competitors/#w7s-vs-vercel" class="hash-link" aria-label="Direct link to W7S vs Vercel" title="Direct link to W7S vs Vercel" translate="no">​</a></h2>
<p>Vercel's normal flow is: connect a Git provider, create a project, let Vercel build every push or pull request, and manage project behavior in Vercel.</p>
<p>W7S's normal flow is: write a GitHub Actions workflow, build the app, and call the W7S deploy action.</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">.github/workflows/deploy.yml</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> Deploy</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">on</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">push</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">branches</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">main</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key atrule">jobs</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token key atrule">deploy</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">runs-on</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> ubuntu</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">latest</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">permissions</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">contents</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> read</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">id-token</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> write</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">steps</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> actions/checkout@v5</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> actions/setup</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">node@v6</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token key atrule">node-version</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token number">22</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> npm ci</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> npm run build</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> w7s</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">io/w7s</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">cloud@v1</span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">          </span><span class="token key atrule">token</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"> github.token </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The practical advantage is control. Build commands, checks, generated files, deploy directory, environment inputs, and deployment permissions are all normal workflow code.</p>
<p>If the deploy changes, the pull request shows it.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="w7s-vs-netlify">W7S vs Netlify<a href="https://w7s.io/docs/blog/vercel-competitors/#w7s-vs-netlify" class="hash-link" aria-label="Direct link to W7S vs Netlify" title="Direct link to W7S vs Netlify" translate="no">​</a></h2>
<p>Netlify is the closest traditional Vercel competitor. It has a strong frontend workflow, deploy previews, functions, scheduled functions, forms, edge features, and a mature product surface.</p>
<p>W7S is better when you do not want the frontend platform to become the app's control plane.</p>
<p>Instead of splitting backend behavior into platform function conventions, W7S uses a native backend:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">backend/index.ts</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">default</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">async</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">fetch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token operator">:</span><span class="token plain"> Request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> env</span><span class="token operator">:</span><span class="token plain"> Env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">const</span><span class="token plain"> url </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name constant" style="color:rgb(189, 147, 249)">URL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">pathname </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/api/health"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">json</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">ok</span><span class="token operator">:</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">pathname </span><span class="token operator">===</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/api/orders"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">handleOrders</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> env</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">Response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Not found"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain">status</span><span class="token operator">:</span><span class="token plain"> </span><span class="token number">404</span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></div></code></pre></div></div>
<p>That shape is easier to grow when the app needs shared middleware, auth, storage, queue helpers, internal RPC, and workflow handling.</p>
<p>Netlify is still a good fit if you want its forms, plugin ecosystem, dashboard workflow, and hosted frontend product.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="w7s-vs-cloudflare-pages">W7S vs Cloudflare Pages<a href="https://w7s.io/docs/blog/vercel-competitors/#w7s-vs-cloudflare-pages" class="hash-link" aria-label="Direct link to W7S vs Cloudflare Pages" title="Direct link to W7S vs Cloudflare Pages" translate="no">​</a></h2>
<p>Cloudflare Pages is a strong Vercel competitor for teams that want Cloudflare's network and frontend deployment model. Pages Functions can add dynamic behavior without a dedicated server.</p>
<p>W7S is different because it is an opinionated app layer above a focused set of platform primitives.</p>
<p>In W7S, the app declares its runtime needs in one manifest:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"bindings"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"db"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"binding"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"DB"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"migrations"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"migrations"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"kv"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"CACHE"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"fs"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"FILES"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"queues"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"jobs"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"workflows"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"checkout"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>Use Cloudflare Pages or raw Workers when you want direct Cloudflare control. Use W7S when you want the app-platform conventions already chosen: deploys, URLs, environments, bindings, queues, internal calls, and usage accounting.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="w7s-vs-render-railway-and-flyio">W7S vs Render, Railway, and Fly.io<a href="https://w7s.io/docs/blog/vercel-competitors/#w7s-vs-render-railway-and-flyio" class="hash-link" aria-label="Direct link to W7S vs Render, Railway, and Fly.io" title="Direct link to W7S vs Render, Railway, and Fly.io" translate="no">​</a></h2>
<p>Render, Railway, and Fly.io are better Vercel competitors when what you really need is process hosting:</p>
<ul>
<li class="">a web service;</li>
<li class="">a worker process;</li>
<li class="">a container image;</li>
<li class="">a machine;</li>
<li class="">custom networking;</li>
<li class="">long-running runtime control.</li>
</ul>
<p>W7S is better when the process is not the point.</p>
<p>Many apps only need request handlers and managed bindings. In that case, running a process platform adds choices you may not need: service size, idle process behavior, worker scaling, process logs, deploy health, container lifecycle, and service orchestration.</p>
<p>W7S replaces those with:</p>
<ul>
<li class="">native backend request handling;</li>
<li class="">queues for async jobs;</li>
<li class="">schedules for cron-style work;</li>
<li class="">workflows for durable processes;</li>
<li class="">DB, KV, FS, and Stateful Objects for storage;</li>
<li class="">GitHub Actions for deployment.</li>
</ul>
<p>Use a process platform when the app needs a process. Use W7S when a process would mostly be packaging around a small app.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="w7s-vs-github-pages">W7S vs GitHub Pages<a href="https://w7s.io/docs/blog/vercel-competitors/#w7s-vs-github-pages" class="hash-link" aria-label="Direct link to W7S vs GitHub Pages" title="Direct link to W7S vs GitHub Pages" translate="no">​</a></h2>
<p>GitHub Pages is excellent for pure static sites.</p>
<p>W7S is better when the site becomes an app:</p>
<ul>
<li class="">form handler;</li>
<li class="">status endpoint;</li>
<li class="">search API;</li>
<li class="">uploaded files;</li>
<li class="">generated data;</li>
<li class="">database-backed content;</li>
<li class="">queue or schedule;</li>
<li class="">branch-isolated previews.</li>
</ul>
<p>The migration is natural because both models start from GitHub. W7S keeps GitHub as the source of truth and adds the runtime GitHub Pages deliberately does not provide.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="w7s-vs-aws-amplify-firebase-and-supabase">W7S vs AWS Amplify, Firebase, and Supabase<a href="https://w7s.io/docs/blog/vercel-competitors/#w7s-vs-aws-amplify-firebase-and-supabase" class="hash-link" aria-label="Direct link to W7S vs AWS Amplify, Firebase, and Supabase" title="Direct link to W7S vs AWS Amplify, Firebase, and Supabase" translate="no">​</a></h2>
<p>AWS Amplify, Firebase, and Supabase are not only deployment platforms. They are backend ecosystems.</p>
<p>They can be the right choice when the backend product is the main thing you want:</p>
<ul>
<li class="">Amplify for AWS-integrated app stacks;</li>
<li class="">Firebase for realtime, mobile, auth, hosting, and Google-backed app services;</li>
<li class="">Supabase for Postgres, auth, realtime, storage, and database-centered apps.</li>
</ul>
<p>W7S is better when you want a smaller deploy platform that gives the repository enough backend capability without adopting a larger backend ecosystem.</p>
<p>The W7S default is intentionally plain:</p>
<ul>
<li class="">a native backend;</li>
<li class="">serverless DB;</li>
<li class="">KV;</li>
<li class="">FS;</li>
<li class="">queues;</li>
<li class="">schedules;</li>
<li class="">workflows;</li>
<li class="">internal RPC;</li>
<li class="">vars and secrets;</li>
<li class="">GitHub Actions deploys.</li>
</ul>
<p>That is not a full Firebase or Supabase replacement. It is a better default when the app wants platform bindings, not a whole backend product universe.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-comparison-that-matters">The Comparison That Matters<a href="https://w7s.io/docs/blog/vercel-competitors/#the-comparison-that-matters" class="hash-link" aria-label="Direct link to The Comparison That Matters" title="Direct link to The Comparison That Matters" translate="no">​</a></h2>
<p>The most important Vercel competitor question is not "which platform has the longest feature list?"</p>
<p>It is:</p>
<blockquote>
<p>Where should deployment truth live?</p>
</blockquote>
<p>If the answer is "inside the platform dashboard," Vercel, Netlify, Cloudflare Pages, Render, Railway, Fly.io, Amplify, Firebase, or Supabase may be a better fit depending on the workload.</p>
<p>If the answer is "inside GitHub and the repository," W7S is the sharper choice.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="when-w7s-is-not-better">When W7S Is Not Better<a href="https://w7s.io/docs/blog/vercel-competitors/#when-w7s-is-not-better" class="hash-link" aria-label="Direct link to When W7S Is Not Better" title="Direct link to When W7S Is Not Better" translate="no">​</a></h2>
<p>Do not choose W7S just because it is simpler.</p>
<p>Vercel may be better when:</p>
<ul>
<li class="">the app is deeply Next.js and benefits from Vercel-specific behavior;</li>
<li class="">your team wants Vercel's dashboard workflow;</li>
<li class="">preview deployment collaboration is already built around Vercel;</li>
<li class="">project integrations and marketplace features matter;</li>
<li class="">the organization already standardizes on Vercel.</li>
</ul>
<p>Other competitors may be better when:</p>
<ul>
<li class="">the app needs containers or long-running processes;</li>
<li class="">the app needs direct Cloudflare account control;</li>
<li class="">the app is pure static and GitHub Pages is enough;</li>
<li class="">the app is really a Firebase, Supabase, or AWS Amplify backend product.</li>
</ul>
<p>W7S is better for a narrower, specific target: GitHub-native apps that want the deployment and runtime contract in code.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="recommendation">Recommendation<a href="https://w7s.io/docs/blog/vercel-competitors/#recommendation" class="hash-link" aria-label="Direct link to Recommendation" title="Direct link to Recommendation" translate="no">​</a></h2>
<p>Choose W7S over Vercel when you want:</p>
<ul>
<li class="">GitHub Actions as the deploy control plane;</li>
<li class="">predictable URLs from GitHub owner and repository;</li>
<li class="">branch environments without a separate project dashboard;</li>
<li class="">static assets and native backend routes in one deployment;</li>
<li class="">storage and background work declared in <code>w7s.json</code>;</li>
<li class="">service-to-service calls based on repository identity;</li>
<li class="">custom domains declared in the repo;</li>
<li class="">an open-source/self-hostable path.</li>
</ul>
<p>Choose Vercel when you want the Vercel product to own the frontend deployment experience.</p>
<p>That is the cleanest distinction. Vercel is a polished hosted frontend platform. W7S is a GitHub-native app platform. If your team wants the repository to stay in charge, W7S is the better Vercel competitor.</p>]]></content:encoded>
            <category>platforms</category>
            <category>alternatives</category>
            <category>vercel</category>
            <category>github-actions</category>
        </item>
        <item>
            <title><![CDATA[Vercel Pricing]]></title>
            <link>https://w7s.io/docs/blog/vercel-pricing/</link>
            <guid>https://w7s.io/docs/blog/vercel-pricing/</guid>
            <pubDate>Sun, 31 May 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[A current, practical comparison of Vercel pricing and W7S pricing for teams deciding whether a GitHub-native deployment model is a better fit.]]></description>
            <content:encoded><![CDATA[<p>Vercel pricing is not one number.</p>
<p>It is a plan plus usage model. The public pricing page currently lists Hobby, Pro, and Enterprise plans. Pro is listed as a monthly plan with additional usage, and Vercel's pricing docs explain that managed infrastructure usage is billed through specific metrics such as data transfer, requests, and compute duration.</p>
<p>W7S is intentionally different:</p>
<blockquote>
<p>W7S starts free without a W7S account, credit card, or separate cloud setup, then aims to price real apps by usage instead of a subscription just to keep them online.</p>
</blockquote>
<p>This article compares the models, not just the sticker prices.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="short-version">Short Version<a href="https://w7s.io/docs/blog/vercel-pricing/#short-version" class="hash-link" aria-label="Direct link to Short Version" title="Direct link to Short Version" translate="no">​</a></h2>
<table><thead><tr><th>Question</th><th>Vercel</th><th>W7S</th></tr></thead><tbody><tr><td>Can I start free?</td><td>Yes, with the Hobby plan</td><td>Yes, without a W7S account or card</td></tr><tr><td>Is there a paid team plan?</td><td>Yes, Pro is listed as a monthly plan plus usage</td><td>W7S is designed around free start and usage-based overage</td></tr><tr><td>What drives cost?</td><td>Plan, seats, bandwidth, requests, compute, builds, observability, storage, images, add-ons, and product-specific meters</td><td>Runtime requests, CPU, storage, DB rows, KV, FS, queues, stateful work, logs, and W7S platform overhead</td></tr><tr><td>Where is deployment configured?</td><td>Vercel project plus Git integration</td><td>GitHub Actions workflow</td></tr><tr><td>Where is runtime shape declared?</td><td>Project settings, framework output, environment variables, and platform products</td><td><code>w7s.json</code> plus backend code</td></tr><tr><td>Best fit</td><td>Teams that want Vercel's managed frontend platform and framework integrations</td><td>Teams that want GitHub-native deploys and app primitives in the repo</td></tr></tbody></table>
<p>Vercel can be the right choice. W7S is better when the repo should own deploy behavior and the app fits static assets plus native backends and managed bindings.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="source-backed-comparison-points">Source-Backed Comparison Points<a href="https://w7s.io/docs/blog/vercel-pricing/#source-backed-comparison-points" class="hash-link" aria-label="Direct link to Source-Backed Comparison Points" title="Direct link to Source-Backed Comparison Points" translate="no">​</a></h2>
<p>The Vercel side of this pricing comparison is based on the live <a href="https://vercel.com/pricing" target="_blank" rel="noopener noreferrer" class="">Vercel pricing page</a>, the <a href="https://vercel.com/docs/pricing" target="_blank" rel="noopener noreferrer" class="">Vercel pricing docs</a>, and <a href="https://vercel.com/docs/spend-management" target="_blank" rel="noopener noreferrer" class="">Vercel Spend Management</a>. Those pages are the source for the plan-plus-usage framing, active compute language, and spend-control discussion.</p>
<p>The backend-cost examples cite Vercel product docs for <a href="https://vercel.com/docs/functions" target="_blank" rel="noopener noreferrer" class="">Vercel Functions</a>, <a href="https://vercel.com/docs/vercel-blob" target="_blank" rel="noopener noreferrer" class="">Vercel Blob</a>, <a href="https://vercel.com/docs/edge-config" target="_blank" rel="noopener noreferrer" class="">Edge Config</a>, <a href="https://vercel.com/docs/queues" target="_blank" rel="noopener noreferrer" class="">Queues</a>, <a href="https://vercel.com/docs/cron-jobs" target="_blank" rel="noopener noreferrer" class="">Cron Jobs</a>, and <a href="https://vercel.com/docs/workflow" target="_blank" rel="noopener noreferrer" class="">Workflow</a>. Those sources explain why a Vercel bill can involve several product-specific meters once a frontend becomes an application.</p>
<p>The W7S side is based on <a class="" href="https://w7s.io/docs/pricing/">W7S pricing</a>, <a class="" href="https://w7s.io/docs/usage-accounting/">Usage Accounting</a>, <a class="" href="https://w7s.io/docs/deploy-from-github/">Deploy From GitHub</a>, and <a class="" href="https://w7s.io/docs/project-layouts/">Project Layouts</a>. Those pages back the argument that W7S cost should be understood through repository-owned deploys and app primitives rather than only an account plan.</p>
<p>The W7S backend model cited here comes from <a class="" href="https://w7s.io/docs/storage-bindings/">Storage Bindings</a>, <a class="" href="https://w7s.io/docs/serverless-database/">Serverless Database</a>, <a class="" href="https://w7s.io/docs/backend-queues/">Backend Queues</a>, <a class="" href="https://w7s.io/docs/backend-schedules/">Backend Schedules</a>, <a class="" href="https://w7s.io/docs/backend-workflows/">Backend Workflows</a>, and <a class="" href="https://w7s.io/docs/backend-rpc/">Backend RPC</a>. These docs support the claim that W7S can replace many small-app backend needs without moving the control plane into a dashboard.</p>
<p>The ownership and escape-hatch claims are backed by <a class="" href="https://w7s.io/docs/urls-and-routing/">URLs and Routing</a> and <a class="" href="https://w7s.io/docs/self-host/">Self Host W7S</a>. Pricing pages change frequently, so the article links to current public sources instead of asking readers to trust a stale table.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-vercel-charges-for">What Vercel Charges For<a href="https://w7s.io/docs/blog/vercel-pricing/#what-vercel-charges-for" class="hash-link" aria-label="Direct link to What Vercel Charges For" title="Direct link to What Vercel Charges For" translate="no">​</a></h2>
<p>As of May 31, 2026, Vercel's public pricing material presents three plan levels:</p>
<ul>
<li class="">Hobby for personal and non-commercial starting points;</li>
<li class="">Pro for professional developers, freelancers, teams, and businesses;</li>
<li class="">Enterprise for custom security, performance, collaboration, support, and SLA needs.</li>
</ul>
<p>The exact bill depends on product usage. Vercel's pricing docs describe managed infrastructure as usage-based, with resources billed by metrics such as data transferred, request count, and compute duration.</p>
<p>Examples on the public pricing page include:</p>
<ul>
<li class="">edge requests;</li>
<li class="">fast data transfer;</li>
<li class="">build minutes;</li>
<li class="">Vercel Functions CPU, memory, and invocations;</li>
<li class="">Blob storage and operations;</li>
<li class="">Image Optimization;</li>
<li class="">Edge Config reads and writes;</li>
<li class="">Workflows;</li>
<li class="">Queues;</li>
<li class="">analytics and observability events;</li>
<li class="">log drains and retention;</li>
<li class="">add-ons such as deployment protection, static IPs, and compliance features.</li>
</ul>
<p>That model is powerful because Vercel has a broad product surface. It also means the bill can come from several places once an app uses more than static hosting.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-w7s-charges-for">What W7S Charges For<a href="https://w7s.io/docs/blog/vercel-pricing/#what-w7s-charges-for" class="hash-link" aria-label="Direct link to What W7S Charges For" title="Direct link to What W7S Charges For" translate="no">​</a></h2>
<p>The W7S pricing page is built around an estimator rather than a simple plan grid.</p>
<p>The hosted service is free to start. You do not need a W7S account, a credit card, or a separate cloud setup to deploy through <code>w7s.cloud</code>. Small apps can deploy, test, demo, and share before billing matters.</p>
<p>When an app starts getting meaningful traffic, W7S is designed to price usage rather than require a subscription just to keep the app online.</p>
<p>The calculator models usage categories such as:</p>
<ul>
<li class="">runtime requests;</li>
<li class="">runtime CPU;</li>
<li class="">deployed runtimes;</li>
<li class="">asset and FS storage;</li>
<li class="">FS read and write operations;</li>
<li class="">key-value reads, writes, and storage;</li>
<li class="">SQL rows read, rows written, and storage;</li>
<li class="">queue operations;</li>
<li class="">Stateful Object requests, duration, and storage;</li>
<li class="">app logs;</li>
<li class="">W7S routing executions and CPU;</li>
<li class="">W7S usage guard reads and counter writes.</li>
</ul>
<p>The calculator subtracts the included baseline first, estimates operating cost, adds W7S platform overhead, and applies the W7S target margin. It is a planning tool, not a final hosted bill.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-important-pricing-difference">The Important Pricing Difference<a href="https://w7s.io/docs/blog/vercel-pricing/#the-important-pricing-difference" class="hash-link" aria-label="Direct link to The Important Pricing Difference" title="Direct link to The Important Pricing Difference" translate="no">​</a></h2>
<p>Vercel pricing starts from a hosted product account and plan. Usage then adds more detail.</p>
<p>W7S starts from a repository and GitHub Actions workflow. Usage is attached to the app primitives the repository asks for.</p>
<p>That changes how a team thinks about cost:</p>
<table><thead><tr><th>Cost question</th><th>Vercel framing</th><th>W7S framing</th></tr></thead><tbody><tr><td>What did we deploy?</td><td>A Vercel project</td><td>A GitHub owner/repo environment</td></tr><tr><td>What created the deploy?</td><td>Vercel Git integration and project settings</td><td>A GitHub Actions workflow</td></tr><tr><td>What backend exists?</td><td>Functions, edge runtime, storage products, integrations</td><td>Native backend plus declared bindings</td></tr><tr><td>What background work exists?</td><td>Product-specific functions, cron, queues, workflows, or add-ons</td><td>Queues, schedules, and workflows in <code>w7s.json</code></td></tr><tr><td>How do previews work?</td><td>Vercel preview deployments</td><td>Branch environments</td></tr><tr><td>How do we inspect usage?</td><td>Vercel usage dashboard and invoices</td><td>W7S usage rollups and GitHub-native warnings</td></tr></tbody></table>
<p>The W7S advantage is not that every meter is cheaper in every case. The advantage is that the app's deploy and runtime surface stay in source control.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="example-small-app-before-traction">Example: Small App Before Traction<a href="https://w7s.io/docs/blog/vercel-pricing/#example-small-app-before-traction" class="hash-link" aria-label="Direct link to Example: Small App Before Traction" title="Direct link to Example: Small App Before Traction" translate="no">​</a></h2>
<p>For a small app, Vercel can be generous. Hobby is useful for personal projects, and Pro includes more professional and team features.</p>
<p>W7S optimizes for a different early-stage path:</p>
<ul>
<li class="">deploy from GitHub Actions;</li>
<li class="">use the GitHub token;</li>
<li class="">get a repo-derived URL;</li>
<li class="">add a backend route when needed;</li>
<li class="">declare DB, KV, FS, queues, schedules, and workflows in <code>w7s.json</code>;</li>
<li class="">stay in the free start path while usage is small.</li>
</ul>
<p>That is why W7S can be better for prototypes, demos, internal tools, and small production apps that do not need a commercial frontend dashboard yet.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="example-app-with-backend-work">Example: App With Backend Work<a href="https://w7s.io/docs/blog/vercel-pricing/#example-app-with-backend-work" class="hash-link" aria-label="Direct link to Example: App With Backend Work" title="Direct link to Example: App With Backend Work" translate="no">​</a></h2>
<p>A frontend app often becomes a backend app gradually:</p>
<ul>
<li class="">contact form;</li>
<li class="">checkout route;</li>
<li class="">signed upload URL;</li>
<li class="">webhook receiver;</li>
<li class="">scheduled sync;</li>
<li class="">queue for email or billing work;</li>
<li class="">database-backed admin UI;</li>
<li class="">service-to-service call.</li>
</ul>
<p>On Vercel, those may map to multiple platform products and meters.</p>
<p>On W7S, they stay inside the W7S app model:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockTitle_OeMC">w7s.json</div><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#F8F8F2;background-color:#282A36"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"bindings"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"db"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"binding"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"DB"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token property">"migrations"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"migrations"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"kv"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"CACHE"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token property">"fs"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"FILES"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"queues"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"mail"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"schedules"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"cron"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"0 * * * *"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token property">"path"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/_w7s/schedules/hourly"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain">  </span><span class="token property">"workflows"</span><span class="token operator">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token string" style="color:rgb(255, 121, 198)">"checkout"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></div><div class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></div></code></pre></div></div>
<p>The usage still matters. The difference is that the repo shows which primitives the app uses.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="spend-controls-and-usage-feedback">Spend Controls and Usage Feedback<a href="https://w7s.io/docs/blog/vercel-pricing/#spend-controls-and-usage-feedback" class="hash-link" aria-label="Direct link to Spend Controls and Usage Feedback" title="Direct link to Spend Controls and Usage Feedback" translate="no">​</a></h2>
<p>Vercel provides spend management tools, budgets, alerts, and controls in its platform.</p>
<p>W7S exposes per-app usage rollups by repository and environment. The W7S deploy action can read usage after a deploy and write warnings into the GitHub Actions summary or open a GitHub issue when an app approaches limits.</p>
<p>That is a different feedback loop. The usage warning appears where the team is already reviewing the repository.</p>
<p>W7S also has free-tier guardrails. These are operational protections, not final billing-grade counters. They keep one app, owner, or global account from unexpectedly consuming shared infrastructure.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="when-vercel-pricing-is-worth-it">When Vercel Pricing Is Worth It<a href="https://w7s.io/docs/blog/vercel-pricing/#when-vercel-pricing-is-worth-it" class="hash-link" aria-label="Direct link to When Vercel Pricing Is Worth It" title="Direct link to When Vercel Pricing Is Worth It" translate="no">​</a></h2>
<p>Vercel can be worth the money when:</p>
<ul>
<li class="">the app is deeply tied to Next.js behavior on Vercel;</li>
<li class="">the team wants Vercel's dashboard workflow;</li>
<li class="">preview collaboration and deployment protection are important;</li>
<li class="">Vercel analytics, observability, security, and add-ons are part of the product workflow;</li>
<li class="">the organization is already standardized on Vercel;</li>
<li class="">the team wants a mature commercial frontend cloud more than repository-owned deployment.</li>
</ul>
<p>The right answer is not always to minimize the bill. Sometimes the hosted workflow is exactly what you are paying for.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="when-w7s-is-better">When W7S Is Better<a href="https://w7s.io/docs/blog/vercel-pricing/#when-w7s-is-better" class="hash-link" aria-label="Direct link to When W7S Is Better" title="Direct link to When W7S Is Better" translate="no">​</a></h2>
<p>W7S is better when:</p>
<ul>
<li class="">you want no W7S account or credit card to start;</li>
<li class="">GitHub Actions should be the deploy control plane;</li>
<li class="">the deploy token should be the GitHub token;</li>
<li class="">the app URL should derive from GitHub owner and repository;</li>
<li class="">branch environments should be predictable;</li>
<li class="">static assets and backend routes should ship together;</li>
<li class="">storage, jobs, schedules, workflows, vars, and secrets should live in the repo contract;</li>
<li class="">usage feedback should return to GitHub;</li>
<li class="">self-hostability matters as an escape hatch.</li>
</ul>
<p>For these teams, the strongest W7S pricing feature is not only cost. It is that the operational model does not become another dashboard-centered subscription before the app has earned that complexity.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="recommendation">Recommendation<a href="https://w7s.io/docs/blog/vercel-pricing/#recommendation" class="hash-link" aria-label="Direct link to Recommendation" title="Direct link to Recommendation" translate="no">​</a></h2>
<p>Use Vercel when you want Vercel's frontend platform, framework integration, collaboration tools, and commercial product surface.</p>
<p>Use W7S when your app can be static assets plus native backend routes and managed bindings, and when you want deployment truth to live in GitHub.</p>
<p>The pricing comparison starts with dollars, but the durable decision is ownership: Vercel rents you a polished deployment product. W7S keeps the deploy workflow and runtime contract closer to the repository.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="current-pricing-sources">Current Pricing Sources<a href="https://w7s.io/docs/blog/vercel-pricing/#current-pricing-sources" class="hash-link" aria-label="Direct link to Current Pricing Sources" title="Direct link to Current Pricing Sources" translate="no">​</a></h2>
<p>Pricing changes. Check the current public pages before making a final cost decision:</p>
<ul>
<li class=""><a href="https://vercel.com/pricing" target="_blank" rel="noopener noreferrer" class="">Vercel pricing</a></li>
<li class=""><a href="https://vercel.com/docs/pricing" target="_blank" rel="noopener noreferrer" class="">Vercel pricing docs</a></li>
<li class=""><a class="" href="https://w7s.io/docs/pricing/">W7S pricing calculator</a></li>
<li class=""><a class="" href="https://w7s.io/docs/usage-accounting/">W7S usage accounting</a></li>
</ul>]]></content:encoded>
            <category>platforms</category>
            <category>pricing</category>
            <category>vercel</category>
            <category>alternatives</category>
        </item>
    </channel>
</rss>