<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Dominic Rockson</title>
    <description>The latest articles on DEV Community by Dominic Rockson (@psrockstar098).</description>
    <link>https://dev.to/psrockstar098</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4151129%2F793e0f91-86d3-4069-b8fa-35d37a5ae9ff.jpg</url>
      <title>DEV Community: Dominic Rockson</title>
      <link>https://dev.to/psrockstar098</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/psrockstar098"/>
    <language>en</language>
    <item>
      <title>Your first transactional email shouldn't need a new account</title>
      <dc:creator>Dominic Rockson</dc:creator>
      <pubDate>Sun, 08 Nov 2026 11:53:20 +0000</pubDate>
      <link>https://dev.to/psrockstar098/your-first-transactional-email-shouldnt-need-a-new-account-5g0b</link>
      <guid>https://dev.to/psrockstar098/your-first-transactional-email-shouldnt-need-a-new-account-5g0b</guid>
      <description>&lt;p&gt;Every side project eventually needs to send an email: a welcome message, a password reset, a receipt. And that's usually the moment the project quietly grows a second project — a mail-service account, API keys, a template system, a test setup, and the question of what happens when the provider is down.&lt;/p&gt;

&lt;p&gt;Yatta JS keeps email inside the backend. It's a backend-as-a-server for Bun: one process with your database, auth, mail, storage, realtime, jobs and observability baked in. The mail engine is already there — &lt;code&gt;createMailer&lt;/code&gt; from &lt;code&gt;yatta.js/mail&lt;/code&gt; — and it starts in terminal mode, so in development your emails print to the console instead of going anywhere. Nothing to sign up for to write your first welcome email.&lt;/p&gt;

&lt;p&gt;When you're ready for real delivery, you change one line: mode becomes &lt;code&gt;smtp&lt;/code&gt; in production and you pick a provider preset — Resend, Postmark, SendGrid, SES, Gmail, Mailgun, Brevo — which fills in host, port and TLS for you. The only things you add are credentials and a From address on a domain you've verified. That's the whole integration.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createMailer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;yatta.js/mail&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mailer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createMailer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;defaultFrom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Yatta App &amp;lt;hello@yourdomain.com&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NODE_ENV&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;production&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;smtp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;terminal&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;resend&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Templates stay typed: you register a template once with its data shape, and sending it is one call.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mailer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user@example.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Welcome to Yatta!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;welcome&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Alice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;verifyUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://app.dev/verify?token=abc&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Markdown in, HTML plus an automatic plain-text version out. There's a preview call that renders the email without sending anything, and a memory mode for tests where you can assert on what was "sent" without touching the network.&lt;/p&gt;

&lt;p&gt;The part I like most is the boring safety that comes free: header values are stripped of CR and LF so a subject can't inject extra headers, template values are escaped by default, there's RFC 8058 one-click unsubscribe, idempotency keys, retries for transient failures, and rate limiting you configure in the same object. And the docs' own advice is to send from a job, so a mail failure can't fail a user request — the same job queue that's already in the process.&lt;/p&gt;

&lt;p&gt;None of this removes the real work of email — you still have to verify your domain and publish SPF, DKIM and DMARC, or providers will drop your mail. What a bundled backend removes is the week spent assembling a mail stack before your app has sent its first real email.&lt;/p&gt;

&lt;p&gt;If your side project has been sitting without a welcome email because the mail setup looked like a whole project, that's the test. Start from a backend where email is already in it, and see how much sooner the first real email goes out.&lt;/p&gt;

&lt;p&gt;Yatta JS is open source under MIT, on npm as yatta.js (1.0.5). Docs: &lt;a href="https://yatta.js.org" rel="noopener noreferrer"&gt;https://yatta.js.org&lt;/a&gt; — Code: &lt;a href="https://github.com/psrockstar098/yatta.js" rel="noopener noreferrer"&gt;https://github.com/psrockstar098/yatta.js&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What's the email your project keeps postponing — welcome, password reset, or receipts?&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>bunjs</category>
      <category>opensource</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Auth shouldn't be a second project: passkeys, MFA and sessions in one Yatta JS process</title>
      <dc:creator>Dominic Rockson</dc:creator>
      <pubDate>Wed, 04 Nov 2026 11:51:29 +0000</pubDate>
      <link>https://dev.to/psrockstar098/auth-shouldnt-be-a-second-project-passkeys-mfa-and-sessions-in-one-yatta-js-process-4l8k</link>
      <guid>https://dev.to/psrockstar098/auth-shouldnt-be-a-second-project-passkeys-mfa-and-sessions-in-one-yatta-js-process-4l8k</guid>
      <description>&lt;p&gt;Most side projects don't die on the main feature. They die on the second project hiding inside the first one: auth.&lt;/p&gt;

&lt;p&gt;You start out building the thing you actually care about, and then a week disappears into sessions, password resets, login pages, and the question of where tokens live. Add passkeys or MFA and that's another library, another set of docs, another thing to keep in sync with the rest of your backend.&lt;/p&gt;

&lt;p&gt;Yatta JS takes a different shape. It's a backend-as-a-server for Bun: one process with your database, auth, mail, storage, realtime, jobs and observability baked in. Auth isn't a service you bolt on — it sits in the same process as the data it protects, next to the same database and the same job queue.&lt;/p&gt;

&lt;p&gt;That changes the order of work. Instead of wiring up an auth provider, then a database, then a session store, and then debugging how the three talk to each other, you start with a backend that already has auth in it — including passkeys and MFA — and you go straight to building the feature your users will actually see.&lt;/p&gt;

&lt;p&gt;The practical payoff is smaller, not grander. Fewer moving parts means fewer places for a login flow to break at 2am. One process means one log stream when something does go wrong. And when you're ready to ship, the same CLI that checks the rest of the backend — doctor, migrate, backup — covers the whole thing, auth included.&lt;/p&gt;

&lt;p&gt;None of this replaces good auth judgment. You still have to decide who can do what in your app, and you still have to think about recovery when someone loses a device. What a bundled backend removes is the plumbing tax: the weeks spent assembling an auth stack before you've shipped anything worth protecting.&lt;/p&gt;

&lt;p&gt;If you're building a solo project and auth is the part you've been putting off, that's the test. Try starting from a backend where auth is already there, and see how much sooner you get to the part you actually wanted to build.&lt;/p&gt;

&lt;p&gt;Yatta JS is open source under MIT, on npm as yatta.js. Docs: &lt;a href="https://yatta.js.org" rel="noopener noreferrer"&gt;https://yatta.js.org&lt;/a&gt; — Code: &lt;a href="https://github.com/psrockstar098/yatta.js" rel="noopener noreferrer"&gt;https://github.com/psrockstar098/yatta.js&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What's the auth feature you keep postponing — passkeys, MFA, or just getting sessions right?&lt;/p&gt;

</description>
      <category>bunjs</category>
      <category>opensource</category>
      <category>showdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Your prototype doesn't need six services — it needs one process</title>
      <dc:creator>Dominic Rockson</dc:creator>
      <pubDate>Sun, 01 Nov 2026 11:51:35 +0000</pubDate>
      <link>https://dev.to/psrockstar098/your-prototype-doesnt-need-six-services-it-needs-one-process-42fp</link>
      <guid>https://dev.to/psrockstar098/your-prototype-doesnt-need-six-services-it-needs-one-process-42fp</guid>
      <description>&lt;p&gt;Most of my prototypes never died because the idea was bad. They died before they got a fair test, buried under the setup.&lt;/p&gt;

&lt;p&gt;A database here. Auth somewhere else. A queue service, a mail provider, a realtime layer, a place to watch logs. Each one is small on its own. Together, before a single user has touched the thing, I am maintaining six services for an idea I have not validated yet.&lt;/p&gt;

&lt;p&gt;That is a strange trade for a prototype. The whole point of a prototype is to learn fast. Every service I add before launch is a decision I have to maintain, a credential to rotate, and a dashboard to check — all for an app that might get ten users this week.&lt;/p&gt;

&lt;p&gt;Yatta JS is the backend I reach for now when the idea is small but the deadline is real. It is a backend-as-a-server for Bun: one process with your database, auth, mail, storage, realtime, jobs, and observability baked in. No build step — TypeScript runs directly. The prototype and its backend are the same thing, not a diagram of services I have to keep in sync.&lt;/p&gt;

&lt;p&gt;What changes in practice is the order of work. Instead of spending the first evening wiring services together, I spend it on the actual feature: the data model, the realtime update, the job that runs after signup. If the prototype works, it is already running on a real backend, not a mock I have to rebuild later. If it does not, I have lost an evening, not a week of infrastructure.&lt;/p&gt;

&lt;p&gt;There is a real trade-off, and I will not hide it. One process is not the right shape for every workload, and a prototype that finds real traction will eventually outgrow it. But that is a good problem — it means the idea survived contact with users. Most prototypes never get there, and the ones that die from setup overhead never even get the chance.&lt;/p&gt;

&lt;p&gt;So my rule now is simple: the prototype gets one process. If it earns more, it can earn more infrastructure later.&lt;/p&gt;

&lt;p&gt;Yatta JS is open source under MIT, on npm as yatta.js. Docs: &lt;a href="https://yatta.js.org" rel="noopener noreferrer"&gt;https://yatta.js.org&lt;/a&gt; — Code: &lt;a href="https://github.com/psrockstar098/yatta.js" rel="noopener noreferrer"&gt;https://github.com/psrockstar098/yatta.js&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What kills your prototypes: the setup, or something else?&lt;/p&gt;

</description>
      <category>bunjs</category>
      <category>opensource</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Your side project doesn't need a database server</title>
      <dc:creator>Dominic Rockson</dc:creator>
      <pubDate>Wed, 28 Oct 2026 11:51:45 +0000</pubDate>
      <link>https://dev.to/psrockstar098/your-side-project-doesnt-need-a-database-server-44c7</link>
      <guid>https://dev.to/psrockstar098/your-side-project-doesnt-need-a-database-server-44c7</guid>
      <description>&lt;p&gt;Every side project I start used to begin the same way: pick a framework, then pick a database, then figure out where that database lives. A hosted Postgres for the real thing, something else locally, connection strings in three places, and a migration story to sort out before I'd written a single feature. The database decision alone could eat the first evening.&lt;/p&gt;

&lt;p&gt;Yatta JS removes that first evening. It's a backend-as-a-server for Bun, and the database is already inside it — a built-in database with a type-safe ORM, sitting in the same process as the rest of the backend. There is no database server to provision, no connection string to babysit, and no separate service to keep awake for a project with twelve users.&lt;/p&gt;

&lt;p&gt;That changes how a first feature feels. The data your API serves lives next to the code that serves it. You define the shape, you read and write it from the same runtime, and the thing you deploy is one process, not a diagram of services. Auth, mail, storage, realtime, jobs, and observability live in that same process too, so the usual "I'll add that later" pieces are already there when later arrives.&lt;/p&gt;

&lt;p&gt;The honest boundary: a built-in database is the right call at solo and small-team scale. It is not a claim that one process replaces a dedicated database cluster forever — if a project ever outgrows it, that means it found users, which is a good problem. But most side projects never die from scale. They die from setup, and a database server is often the first piece of setup that kills the momentum.&lt;/p&gt;

&lt;p&gt;So the rule I use now: if the project is small, keep the database in the app. Yatta JS is on npm as yatta.js — install it with &lt;code&gt;npm i yatta.js&lt;/code&gt; — and the docs are at &lt;a href="https://yatta.js.org" rel="noopener noreferrer"&gt;https://yatta.js.org&lt;/a&gt;. Code: &lt;a href="https://github.com/psrockstar098/yatta.js" rel="noopener noreferrer"&gt;https://github.com/psrockstar098/yatta.js&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What killed your last side project's momentum — the database setup, auth, or something else?&lt;/p&gt;

</description>
      <category>bunjs</category>
      <category>opensource</category>
      <category>showdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Before you ship your side project, run three commands: Yatta JS's doctor, migrate, and backup</title>
      <dc:creator>Dominic Rockson</dc:creator>
      <pubDate>Sat, 24 Oct 2026 11:51:34 +0000</pubDate>
      <link>https://dev.to/psrockstar098/before-you-ship-your-side-project-run-three-commands-yatta-jss-doctor-migrate-and-backup-1ne8</link>
      <guid>https://dev.to/psrockstar098/before-you-ship-your-side-project-run-three-commands-yatta-jss-doctor-migrate-and-backup-1ne8</guid>
      <description>&lt;p&gt;Side projects rarely die because the idea was bad. They die in the gap between "it works on my machine" and "it survives real users" — the database migration nobody ran, the backup nobody took, the misconfiguration nobody noticed until launch day.&lt;/p&gt;

&lt;p&gt;Yatta JS is a backend-as-a-server for Bun: database, auth, mail, storage, realtime, jobs, and observability embedded in your application process instead of provisioned as separate services. It is live as &lt;a href="mailto:yatta.js@1.0.0"&gt;yatta.js@1.0.0&lt;/a&gt; on npm — install it with npm i yatta.js — and the docs are at &lt;a href="https://yatta.js.org" rel="noopener noreferrer"&gt;https://yatta.js.org&lt;/a&gt;. What I want to talk about today is not the architecture, though. It is the three boring commands that sit between a demo and a shipped thing.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Doctor first: check the patient before the launch&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Every framework has a setup guide. Few of them will look at your actual project and tell you what is off. Yatta's doctor command does that job: it checks your project setup and flags problems before they become launch-day surprises.&lt;/p&gt;

&lt;p&gt;My rule now is simple: doctor runs before every deploy, not after something breaks. It is the cheapest form of confidence a solo builder can buy, and it costs nothing because the check is local to your own project. If you take one habit from this post, take this one — verify the setup, don't assume it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Migrations as a normal part of the workflow, not a scary event&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A side project that never changes its data model is a side project that never ships a second feature. Yatta's CLI treats migrations as ordinary work: make a migration, check migration status, run the migration. That rhythm matters more than any single command, because it turns schema changes from a dreaded manual step into a repeatable one you do without thinking twice.&lt;/p&gt;

&lt;p&gt;The practical payoff is unglamorous and real: your local database, your staging data, and your production data move forward in the same steps, in the same order. When you are the only person on the project, that repeatability is your whole ops team.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Backup before you need it, not after you lose something&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Yatta's CLI includes database backup and restore commands, and the honest truth is that most solo projects never test a restore until the day they need one. Build the habit the other way round: take a backup before a risky migration, and know that restore is a command you have already practised, not a README you are reading in a panic.&lt;/p&gt;

&lt;p&gt;That is the whole pre-ship checklist, and it fits in a short paragraph: run doctor, keep migrations routine, back up before risky changes. None of it is exciting. All of it is what keeps a one-person project alive past week two.&lt;/p&gt;

&lt;p&gt;None of this replaces good judgement — a backup you never tested is a hope, not a plan, and a migration you never reviewed is still your responsibility. But having doctor, migrate, and backup as first-class commands in the same runtime that already holds your database, realtime, and jobs means the unglamorous work stops being a separate project. It is just part of shipping.&lt;/p&gt;

&lt;p&gt;If you are building on Bun and you are tired of assembling a backend from six services before you write a single feature, Yatta JS is worth a look: &lt;a href="https://yatta.js.org" rel="noopener noreferrer"&gt;https://yatta.js.org&lt;/a&gt; — npm: &lt;a href="https://www.npmjs.com/package/yatta.js" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/yatta.js&lt;/a&gt; — code: &lt;a href="https://github.com/psrockstar098/yatta.js" rel="noopener noreferrer"&gt;https://github.com/psrockstar098/yatta.js&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;What is the one pre-launch check you always skip and always regret? I would genuinely like to steal better habits from the comments.&lt;/p&gt;

</description>
      <category>bunjs</category>
      <category>opensource</category>
      <category>showdev</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>The single-process backend: how Yatta JS collapses ten engines into one Bun process</title>
      <dc:creator>Dominic Rockson</dc:creator>
      <pubDate>Tue, 20 Oct 2026 16:26:15 +0000</pubDate>
      <link>https://dev.to/psrockstar098/the-single-process-backend-how-yatta-js-collapses-ten-engines-into-one-bun-process-309d</link>
      <guid>https://dev.to/psrockstar098/the-single-process-backend-how-yatta-js-collapses-ten-engines-into-one-bun-process-309d</guid>
      <description>&lt;p&gt;I launched Yatta JS 1.0 a few days ago. That post was the what. This one is the how.&lt;/p&gt;

&lt;p&gt;Most backends are a pile of services glued together. Database here, auth service there, queue, cache, mail provider, realtime layer, monitoring. Each one is a network hop, a config, a bill, a 2am failure.&lt;/p&gt;

&lt;p&gt;Yatta JS takes the opposite bet: the whole backend is one Bun process. Ten engines share it behind a single yatta/api entry point.&lt;/p&gt;

&lt;p&gt;What ten engines means in practice: a type-safe SQLite ORM, auth with passkeys and TOTP two factor on Argon2id hashes and AES-256-GCM sessions, durable job queues with cron and a dead letter queue, L1 plus SQLite L2 cache with stale-while-revalidate, local and S3-compatible storage, transactional mail, unified SSE and WebSocket realtime, SLO observability, and an OpenTelemetry bridge.&lt;/p&gt;

&lt;p&gt;One process does not mean one thread doing everything. The worker runtime sizes its CPU and IO pools from the host core count instead of spawning a worker per core. It runs sane on a two-core VPS and a 64-core box with no config changes.&lt;/p&gt;

&lt;p&gt;Sharing a process also means sharing memory, and that is where the discipline lives. Streamed request bodies get byte caps so one huge upload cannot eat the process. Token consumption is atomic so two workers never spend the same token. Rate limiting is bounded so forged keys cannot blow up the limiter state. These are the boring details that decide whether single-process is a feature or a footgun.&lt;/p&gt;

&lt;p&gt;There is no build step. The TypeScript you write is the TypeScript that runs. One entry point, one process, one runtime to deploy.&lt;/p&gt;

&lt;p&gt;The numbers: 889 tests passing. Benchmarks run in CI on every push, and the current numbers are public. On my machine: Yatta JS 12.4k req/s, Hono 13.7k, Elysia 15.9k. On CI runners: Yatta JS 33.6k, Hono 38.6k, Elysia 50k. Slower than the pure routers, on purpose. Every request here passes through a runtime doing real work.&lt;/p&gt;

&lt;p&gt;MIT licensed, free forever. Built solo in Liberia.&lt;/p&gt;

&lt;p&gt;Docs: &lt;a href="https://yatta.js.org" rel="noopener noreferrer"&gt;https://yatta.js.org&lt;/a&gt;&lt;br&gt;
Code: &lt;a href="https://github.com/psrockstar098/yatta.js" rel="noopener noreferrer"&gt;https://github.com/psrockstar098/yatta.js&lt;/a&gt;&lt;br&gt;
Install: npm i yatta.js&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>bunjs</category>
      <category>architecture</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Yatta JS 1.0 is now live on npm</title>
      <dc:creator>Dominic Rockson</dc:creator>
      <pubDate>Sat, 17 Oct 2026 14:30:38 +0000</pubDate>
      <link>https://dev.to/psrockstar098/yatta-js-is-live-i-stopped-deploying-backends-and-started-shipping-them-inside-the-runtime-4ij</link>
      <guid>https://dev.to/psrockstar098/yatta-js-is-live-i-stopped-deploying-backends-and-started-shipping-them-inside-the-runtime-4ij</guid>
      <description>&lt;p&gt;Yatta JS is a backend-as-a-server for Bun: one runtime with your database, auth, mail, storage, realtime, jobs and observability baked in. No build step — TypeScript runs directly. Open source under MIT, built solo in Liberia. Yatta JS 1.0 is now live on npm — install it with npm i yatta.js: &lt;a href="https://www.npmjs.com/package/yatta.js" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/yatta.js&lt;/a&gt;. Docs: &lt;a href="https://yatta.js.org" rel="noopener noreferrer"&gt;https://yatta.js.org&lt;/a&gt; — Code: &lt;a href="https://github.com/psrockstar098/yatta.js" rel="noopener noreferrer"&gt;https://github.com/psrockstar098/yatta.js&lt;/a&gt;&lt;/p&gt;

</description>
      <category>bunjs</category>
      <category>opensource</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Yatta JS launches Sunday, October 4</title>
      <dc:creator>Dominic Rockson</dc:creator>
      <pubDate>Thu, 01 Oct 2026 18:56:36 +0000</pubDate>
      <link>https://dev.to/psrockstar098/yatta-js-launches-sunday-october-4-1jm4</link>
      <guid>https://dev.to/psrockstar098/yatta-js-launches-sunday-october-4-1jm4</guid>
      <description>&lt;p&gt;Quick heads up for the weekend. I am releasing Yatta JS on Sunday, October 4.&lt;/p&gt;

&lt;p&gt;It is a backend runtime for Bun that embeds the whole stack in your app process: relational DB, passkeys and MFA auth, storage, realtime websockets, durable job queues, transactional mail, plus observability. Boots in under 10ms. No Docker, no brokers.&lt;/p&gt;

&lt;p&gt;APIs read like plain English and you get full type safety with zero codegen.&lt;/p&gt;

&lt;p&gt;Full launch post drops Sunday.&lt;/p&gt;

</description>
      <category>backend</category>
      <category>javascript</category>
      <category>showdev</category>
    </item>
    <item>
      <title>How rockzy-link's prefetching actually works under the hood</title>
      <dc:creator>Dominic Rockson</dc:creator>
      <pubDate>Thu, 01 Oct 2026 11:30:00 +0000</pubDate>
      <link>https://dev.to/psrockstar098/how-rockzy-links-prefetching-actually-works-under-the-hood-3elb</link>
      <guid>https://dev.to/psrockstar098/how-rockzy-links-prefetching-actually-works-under-the-hood-3elb</guid>
      <description>&lt;p&gt;My first post was the announcement. This one is for the curious: how does rockzy-link actually make navigation feel instant? Let's go under the hood.&lt;/p&gt;

&lt;h2&gt;
  
  
  There are two paths, not one
&lt;/h2&gt;

&lt;p&gt;Most navigation code thinks in one path: click, then fetch, then render. rockzy-link splits it in two:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Prefetch path&lt;/strong&gt; — something signals intent (hover, focus, a link entering the viewport, browser idle time, or pointerdown). The scheduler starts loading what the route will need.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Navigation path&lt;/strong&gt; — the actual click. By then the data and route chunks are usually already warm, so the navigation just runs its checks, guards, and router delegation, then renders.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The insight is boring but true: the fastest request is the one that finished before you asked for it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Intent detection: how it knows you might click
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;prefetch&lt;/code&gt; prop on &lt;code&gt;&amp;lt;Link&amp;gt;&lt;/code&gt; accepts modes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;hover&lt;/code&gt; (the default) — best for high-confidence intent: menus, sidebars, expensive pages&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;viewport&lt;/code&gt; — starts prefetching when the link scrolls into view&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;idle&lt;/code&gt; — uses browser idle time, so it never competes with real work&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;none&lt;/code&gt; / &lt;code&gt;false&lt;/code&gt; — off&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And there's one more trick: &lt;strong&gt;pointerdown&lt;/strong&gt;. When you press the mouse button, the click hasn't happened yet. That gap between press and release is a few dozen to a few hundred milliseconds of free time — rockzy-link fires a high-priority prefetch there too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/dashboard"&lt;/span&gt; &lt;span class="na"&gt;prefetch&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"hover"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  Dashboard
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/reports"&lt;/span&gt; &lt;span class="na"&gt;prefetch&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  Reports
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The prefetch scheduler
&lt;/h2&gt;

&lt;p&gt;All of those signals funnel into a smart prefetch scheduler (&lt;code&gt;rockzy-link/prefetch&lt;/code&gt;). It doesn't just fire requests blindly:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It &lt;strong&gt;dedupes&lt;/strong&gt; prefetch requests across links and tabs, so ten links to the same route don't trigger ten fetches.&lt;/li&gt;
&lt;li&gt;It stays within the &lt;strong&gt;browser's connection budget&lt;/strong&gt; — only a few requests can be in flight at once, and prefetching must never starve the real navigation or the user's current page.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the part people miss. Prefetching isn't hard. &lt;em&gt;Polite&lt;/em&gt; prefetching is hard.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually gets prefetched and cached
&lt;/h2&gt;

&lt;p&gt;Prefetching a route means warming its &lt;strong&gt;route data&lt;/strong&gt;: the JSON/data the page needs, or the HTML/RSC payload depending on the app. rockzy-link stores this in a smart cache with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TTL&lt;/strong&gt; — fresh data for a window you control&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;stale-while-revalidate&lt;/strong&gt; — serve instantly, refresh in the background&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;tags&lt;/strong&gt; — group cache entries and invalidate a whole group at once&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;mutation invalidation&lt;/strong&gt; — when you mutate data, invalidate the tags it affects so you never render stale UI
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;routeCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;ttlMs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                &lt;span class="c1"&gt;// fresh for 1 minute&lt;/span&gt;
  &lt;span class="na"&gt;staleWhileRevalidateMs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// serve stale for 5 more, revalidate behind&lt;/span&gt;
  &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;home&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="c1"&gt;// later, after a mutation:&lt;/span&gt;
&lt;span class="nx"&gt;routeCache&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invalidateTag&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;products&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cache also keeps itself &lt;strong&gt;bounded&lt;/strong&gt; (max 500 entries) so a long-lived tab doesn't eat memory.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happens on click
&lt;/h2&gt;

&lt;p&gt;When the click finally lands, rockzy-link runs the navigation path:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Safety checks and guards — &lt;code&gt;beforeNavigate&lt;/code&gt; hooks can cancel&lt;/li&gt;
&lt;li&gt;Router delegation — works with React Router, TanStack Router, or the built-in one&lt;/li&gt;
&lt;li&gt;Scroll and focus handling — smooth scroll, hash support, focus management for accessibility&lt;/li&gt;
&lt;li&gt;Transitions — View Transitions API support, &lt;code&gt;startTransition&lt;/code&gt; where it helps&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Offline&lt;/strong&gt; — if the network is down, it serves the cached route instead of a blank page, and queues mutations for later&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then &lt;code&gt;onSuccess&lt;/code&gt; fires. If everything was prefetched, steps 1-4 are the only real cost — and navigation feels instant because the data fetch, the slow part, already happened.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fastest possible path
&lt;/h2&gt;

&lt;p&gt;If you just want raw speed with no router ceremony:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;runtime&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rockzy-link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;navigate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/dashboard&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;scroll&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;announce&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;focus&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why this matters more than bundle size
&lt;/h2&gt;

&lt;p&gt;For the record: the prefetch scheduler is ~4.24 KB brotli, and core + link is ~2.70 KB. Small. But size isn't the point. Perceived performance is about &lt;em&gt;when&lt;/em&gt; work happens, not how much work there is. Move the fetch earlier — to the moment of intent instead of the moment of click — and the same app feels like a different app.&lt;/p&gt;

&lt;p&gt;Try it out — it's live on Product Hunt today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Product Hunt: &lt;a href="https://www.producthunt.com/products/rockzy-link?launch=rockzy-link" rel="noopener noreferrer"&gt;https://www.producthunt.com/products/rockzy-link?launch=rockzy-link&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/psrockstar098/rockzy-link" rel="noopener noreferrer"&gt;https://github.com/psrockstar098/rockzy-link&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;npm: &lt;a href="https://www.npmjs.com/package/rockzy-link" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/rockzy-link&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;rockzy-link
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>performance</category>
    </item>
    <item>
      <title>I built a React Link component that prefetches before you click</title>
      <dc:creator>Dominic Rockson</dc:creator>
      <pubDate>Wed, 30 Sep 2026 00:48:39 +0000</pubDate>
      <link>https://dev.to/psrockstar098/i-built-a-react-link-component-that-prefetches-before-you-click-28bj</link>
      <guid>https://dev.to/psrockstar098/i-built-a-react-link-component-that-prefetches-before-you-click-28bj</guid>
      <description>&lt;p&gt;Your app's navigation is only as fast as your Link component. The default one just... navigates. I wanted more, so I built rockzy-link.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Smart prefetching&lt;/strong&gt; — analyzes and preloads routes users are likely to visit next, instead of prefetching everything and wasting bandwidth&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Route caching&lt;/strong&gt; — back/forward navigation feels instant&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scroll restoration&lt;/strong&gt; — users land where they expect&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Offline hooks&lt;/strong&gt; — hooks so your UI degrades gracefully when the network drops&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Navigation snapshots&lt;/strong&gt; — capture and restore navigation state&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accessibility&lt;/strong&gt; — proper semantics out of the box&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i rockzy-link
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Link&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rockzy-link&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Drop-in replacement — prefetching just works&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt; &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/dashboard"&lt;/span&gt; &lt;span class="na"&gt;prefetch&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Dashboard&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Link&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why I built it
&lt;/h2&gt;

&lt;p&gt;I'm a developer from Liberia working mostly in Zig and JavaScript. I kept hitting the same wall: client-side navigation that felt sluggish no matter how optimized the pages were. The bottleneck was the Link itself. So I fixed it at the source.&lt;/p&gt;

&lt;p&gt;It's MIT licensed and I'd genuinely love feedback — try it, break it, tell me what's missing.&lt;/p&gt;

&lt;p&gt;npm: &lt;a href="https://www.npmjs.com/package/rockzy-link" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/rockzy-link&lt;/a&gt;&lt;br&gt;
GitHub: &lt;a href="https://github.com/psrockstar098/rockzy-link" rel="noopener noreferrer"&gt;https://github.com/psrockstar098/rockzy-link&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
