<?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: zintrust Zin</title>
    <description>The latest articles on DEV Community by zintrust Zin (@zintrustjs).</description>
    <link>https://dev.to/zintrustjs</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%2F3669249%2Fd37cd936-f354-42b6-a39f-f68c512ad081.png</url>
      <title>DEV Community: zintrust Zin</title>
      <link>https://dev.to/zintrustjs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zintrustjs"/>
    <language>en</language>
    <item>
      <title>Connect Redis, Postgres, and MySQL to Serverless Apps Over HTTPS</title>
      <dc:creator>zintrust Zin</dc:creator>
      <pubDate>Sat, 11 Jul 2026 07:28:49 +0000</pubDate>
      <link>https://dev.to/zintrustjs/connect-redis-postgres-and-mysql-to-serverless-apps-over-https-cg</link>
      <guid>https://dev.to/zintrustjs/connect-redis-postgres-and-mysql-to-serverless-apps-over-https-cg</guid>
      <description>&lt;p&gt;Serverless and edge runtimes are great until your app needs a normal TCP database connection.&lt;/p&gt;

&lt;p&gt;ZedGi works with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript / JavaScript SDK&lt;/strong&gt;: Cloudflare Workers, Vercel Edge, Deno, Bun, Node.js, AWS Lambda, and any runtime with &lt;code&gt;fetch&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python SDK&lt;/strong&gt;: FastAPI, Flask, Django, scripts, background jobs, AWS Lambda, and other Python services.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Raw HTTP API&lt;/strong&gt;: curl, Go, PHP, Ruby, Java, Rust, no-code tools, internal platforms, and any environment that can send signed HTTPS requests.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cloudflare Workers, Vercel Edge, Deno Deploy, and many other HTTP-first runtimes do not let you open raw TCP sockets directly. That creates an awkward choice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Move to a provider-specific serverless database.&lt;/li&gt;
&lt;li&gt;Add a custom backend just to proxy database calls.&lt;/li&gt;
&lt;li&gt;Expose database ports and manage networking yourself.&lt;/li&gt;
&lt;li&gt;Give up on running that part of the app at the edge.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;ZedGi is built for the other path: keep your own Redis, Postgres, or MySQL database, but call it from any HTTP runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is ZedGi?
&lt;/h2&gt;

&lt;p&gt;ZedGi is a TCP-to-HTTP proxy for Redis, Postgres, and MySQL.&lt;/p&gt;

&lt;p&gt;You register a database service once, then your app sends signed HTTPS requests to your ZedGi endpoint. ZedGi authenticates the request, routes it to a bridge node, opens the real TCP connection, runs the command or query, and returns JSON.&lt;/p&gt;

&lt;p&gt;The basic flow looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Your app
  -&amp;gt; HTTPS request
  -&amp;gt; ZedGi gateway
  -&amp;gt; bridge proxy
  -&amp;gt; Redis / Postgres / MySQL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That means code running in an edge or serverless runtime can still talk to traditional TCP databases without managing VPNs, public database ports, or long-lived connection pools in your function.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why developers use it
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Use the database you already have
&lt;/h3&gt;

&lt;p&gt;ZedGi is not a hosted Redis-only, Postgres-only, or MySQL-only product.&lt;/p&gt;

&lt;p&gt;It is designed for teams that already have a database somewhere and want to make it reachable from HTTP-only runtimes.&lt;/p&gt;

&lt;p&gt;Supported services:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Redis&lt;/li&gt;
&lt;li&gt;Postgres&lt;/li&gt;
&lt;li&gt;MySQL&lt;/li&gt;
&lt;li&gt;Custom TCP services (on request)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Works from any HTTP runtime
&lt;/h3&gt;

&lt;p&gt;If your runtime can make an HTTPS request, it can call ZedGi.&lt;/p&gt;

&lt;p&gt;That includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Cloudflare Workers&lt;/li&gt;
&lt;li&gt;Vercel Edge Functions&lt;/li&gt;
&lt;li&gt;AWS Lambda&lt;/li&gt;
&lt;li&gt;Deno&lt;/li&gt;
&lt;li&gt;Bun&lt;/li&gt;
&lt;li&gt;Node.js&lt;/li&gt;
&lt;li&gt;Any environment with &lt;code&gt;fetch&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Credentials are not stored in ZedGi
&lt;/h3&gt;

&lt;p&gt;Database credentials are supplied at call time by your application.&lt;/p&gt;

&lt;p&gt;The client encrypts the credential payload before it leaves your app. ZedGi stores service metadata like host, port, service type, and optional caller IP allowlist, but not your plaintext database password.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example: Redis from an edge runtime
&lt;/h2&gt;

&lt;p&gt;Install the client:&lt;br&gt;
&lt;/p&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; @zedgi/zedgi-client
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then call Redis over HTTPS:&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;createZedgiClient&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;@zedgi/zedgi-client&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;zedgi&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createZedgiClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;url&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://your-subdomain.zedgi.app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;key&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;ZEDGI_KEY&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;zedgi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;password&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;REDIS_PASSWORD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&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;redis&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;edge:hello&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;world&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;edge:hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// "world"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same pattern works for Postgres and MySQL.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example: Postgres query
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;zedgi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postgres&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;user&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;PG_USER&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;password&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;PG_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;database&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;PG_DATABASE&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;ssl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;rows&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;pg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;select id, email from users where id = $1&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;userId&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;
  
  
  Example: MySQL query
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mysql&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;zedgi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mysql&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;credential&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;user&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;MYSQL_USER&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;password&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;MYSQL_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;database&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;MYSQL_DATABASE&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;rows&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;mysql&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;select id, email from users where id = ?&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;userId&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;
  
  
  What about security?
&lt;/h2&gt;

&lt;p&gt;ZedGi uses a few layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API keys identify the caller.&lt;/li&gt;
&lt;li&gt;Requests are signed to prevent tampering and replay.&lt;/li&gt;
&lt;li&gt;Database credentials are encrypted client-side.&lt;/li&gt;
&lt;li&gt;Optional caller IP allowlisting can restrict which source IP may use a service.&lt;/li&gt;
&lt;li&gt;The proxy bridge only decrypts credentials transiently to open the TCP connection.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is simple: your edge app can call your database, but ZedGi does not become a place where plaintext database passwords sit around.&lt;/p&gt;

&lt;h2&gt;
  
  
  When ZedGi is useful
&lt;/h2&gt;

&lt;p&gt;ZedGi is a good fit when:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You want Cloudflare Workers or edge functions to talk to your existing database.&lt;/li&gt;
&lt;li&gt;You do not want to migrate to a vendor-specific serverless database.&lt;/li&gt;
&lt;li&gt;You need Redis, Postgres, and MySQL behind one HTTPS access layer.&lt;/li&gt;
&lt;li&gt;You have a custom TCP service you want to expose through the same HTTPS proxy model.&lt;/li&gt;
&lt;li&gt;You want a dashboard for API keys, services, usage, and billing.&lt;/li&gt;
&lt;li&gt;You need to keep database ports private.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It is especially useful for internal tools, SaaS dashboards, lightweight APIs, queues, webhook workers, and jobs that need database access from serverless environments.&lt;/p&gt;

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

&lt;p&gt;Create an account, register a service, generate an API key, and make your first call:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://zedgi.app" rel="noopener noreferrer"&gt;https://zedgi.app&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Docs:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://zedgi.app/docs/" rel="noopener noreferrer"&gt;https://zedgi.app/docs/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you have been avoiding edge/serverless because your database still speaks TCP, ZedGi gives you a practical bridge: keep the database, call it over HTTPS.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>database</category>
      <category>redis</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Cloudflare Queues + Real Job Queue Features (without Redis)</title>
      <dc:creator>zintrust Zin</dc:creator>
      <pubDate>Fri, 12 Jun 2026 09:51:53 +0000</pubDate>
      <link>https://dev.to/zintrustjs/cloudflare-queues-real-job-queue-features-without-redis-5h7i</link>
      <guid>https://dev.to/zintrustjs/cloudflare-queues-real-job-queue-features-without-redis-5h7i</guid>
      <description>&lt;p&gt;f you build on Cloudflare Workers today and you need background jobs, you have probably had the following conversation:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"We can use Cloudflare Queues for delivery, it's basically free and global."&lt;br&gt;
"Yeah but then how do we know which jobs are pending? How do we do repeatables? Rate limiting per tenant? Proper backoff with visibility?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The honest answer used to be: "you stand up Redis (or SQS) for that part."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.npmjs.com/package/@zintrust/queue-cloudflare" rel="noopener noreferrer"&gt;@zintrust/queue-cloudflare&lt;/a&gt;&lt;/strong&gt; is our attempt to make the honest answer better.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two usage modes
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Dead simple (no extra state)
&lt;/h3&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;Queue&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;@zintrust/core/queue&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;Queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enqueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EMAIL_QUEUE&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="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&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="s1"&gt;welcome&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;cloudflare&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;This just uses Cloudflare Queues directly (via binding or the REST API). Great for fire-and-forget or when you already have your own tracking.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Full state layer (the interesting one)
&lt;/h3&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;CloudflareQueues&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;@zintrust/queue-cloudflare&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;CloudflareQueues&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;driver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cloudflare&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;bindingName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;EMAIL_QUEUE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;d1BindingName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;QUEUE_DB&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;kvBindingName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;QUEUE_KV&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;           &lt;span class="c1"&gt;// optional but recommended&lt;/span&gt;
    &lt;span class="na"&gt;coordinatorBindingName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;QUEUE_COORDINATOR&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="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;job&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email-jobs&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;send-welcome&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&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;attempts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;backoff&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;exponential&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2000&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;delay&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="na"&gt;deduplication&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`welcome:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;86&lt;/span&gt;&lt;span class="nx"&gt;_400_000&lt;/span&gt; &lt;span class="p"&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;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getJobCounts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;email-jobs&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;waiting&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;active&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;failed&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;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateProgress&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;step&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rendering&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;job&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;template loaded&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="na"&gt;templateId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under the hood:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A tiny envelope (protocol + jobId + queue + attempt + availableAt) is what actually travels through Cloudflare Queues.&lt;/li&gt;
&lt;li&gt;The full job row, attempts, progress, result, error, logs, and flow links live in D1.&lt;/li&gt;
&lt;li&gt;Coordination (leases, heartbeats, rate limiting, pause gate) happens in a Durable Object with SQLite storage.&lt;/li&gt;
&lt;li&gt;The scheduler (delayed + repeatables + stalled reconciliation) runs from your cron trigger and reads/writes D1.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What you actually get
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Job lifecycle you can query and act on (&lt;code&gt;getJob&lt;/code&gt;, &lt;code&gt;retry&lt;/code&gt;, &lt;code&gt;promote&lt;/code&gt;, &lt;code&gt;remove&lt;/code&gt;, &lt;code&gt;clean&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Repeatable jobs via cron or fixed interval + &lt;code&gt;limit&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Parent/child flows (&lt;code&gt;createFlow&lt;/code&gt; + automatic parent release)&lt;/li&gt;
&lt;li&gt;Per-job progress + structured logs&lt;/li&gt;
&lt;li&gt;Token-bucket rate limiting + queue pause/resume (enforced before dispatch)&lt;/li&gt;
&lt;li&gt;Stalled job recovery&lt;/li&gt;
&lt;li&gt;Retention policies (&lt;code&gt;removeOnComplete&lt;/code&gt; / &lt;code&gt;removeOnFail&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Clean consumer helper that gives you &lt;code&gt;updateProgress&lt;/code&gt;, &lt;code&gt;log&lt;/code&gt;, and &lt;code&gt;heartbeat&lt;/code&gt; in context&lt;/li&gt;
&lt;li&gt;DLQ integration (recommended)&lt;/li&gt;
&lt;li&gt;Same API surface whether you're running locally with &lt;code&gt;zin s&lt;/code&gt; or in Workers with &lt;code&gt;--wg&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The limits (read these)
&lt;/h2&gt;

&lt;p&gt;Cloudflare Queues is an at-least-once system with its own constraints. We document the practical trade-offs directly so you don’t get surprised in production.&lt;/p&gt;

&lt;p&gt;Important realities:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;At-least-once&lt;/strong&gt;. Your job processors must be idempotent. Use the &lt;code&gt;jobId&lt;/code&gt;, &lt;code&gt;deduplication&lt;/code&gt;, and DLQ tooling we give you.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;128 KB&lt;/strong&gt; maximum size for messages on Cloudflare Queues. Keep envelopes small. (&lt;a href="https://developers.cloudflare.com/queues/platform/limits/" rel="noopener noreferrer"&gt;limits&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Retention on the queue itself is configurable up to 14 days (4 days default on paid plans; 24h non-configurable on the free plan). (&lt;a href="https://developers.cloudflare.com/queues/platform/limits/" rel="noopener noreferrer"&gt;limits&lt;/a&gt;, &lt;a href="https://developers.cloudflare.com/queues/configuration/configure-queues/#queue-configuration" rel="noopener noreferrer"&gt;configuration&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Scheduling and reconciliation run on whatever cron you configure (commonly every minute). You can add DO alarms for finer grain if you need it.&lt;/li&gt;
&lt;li&gt;Pause/resume is a dispatch gate. Already-delivered messages in a batch will still execute.&lt;/li&gt;
&lt;li&gt;You must provision and bind: the Queue(s), a D1, the coordinator Durable Object, a cron, and (strongly recommended) a DLQ.&lt;/li&gt;
&lt;li&gt;This is not Redis. Some things (exact priority ordering without multiple queues or scheduler dispatch, sandboxed processors, sub-second timing) are different by design.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If those constraints are acceptable, the upside is huge: you stay inside the Cloudflare billing and operational model instead of running a second stateful system whose only job is "be a queue."&lt;/p&gt;

&lt;h2&gt;
  
  
  Recommended minimal production wiring (wrangler.jsonc sketch)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json-doc"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"queues"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"producers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"queue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email-jobs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"binding"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"EMAIL_QUEUE"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"consumers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"queue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email-jobs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"max_batch_size"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"max_retries"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"dead_letter_queue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email-dlq"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"d1_databases"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"binding"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"QUEUE_DB"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"database_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"zintrust-queue"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"kv_namespaces"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"binding"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"QUEUE_KV"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"durable_objects"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bindings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"QUEUE_COORDINATOR"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"class_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CloudflareQueueCoordinator"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"migrations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"tag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"queue-cloudflare-v1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"new_sqlite_classes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"CloudflareQueueCoordinator"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"triggers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"crons"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"* * * * *"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the migration once:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;zin migrate:queue-cloudflare &lt;span class="nt"&gt;--database&lt;/span&gt; zintrust-queue &lt;span class="nt"&gt;--remote&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then in your consumer Worker use the provided &lt;code&gt;createConsumer&lt;/code&gt; helper.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to use this vs something else
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Use it when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The majority of your stack is already Cloudflare-native.&lt;/li&gt;
&lt;li&gt;You want observable, controllable background work without introducing Redis/SQS as a hard dependency.&lt;/li&gt;
&lt;li&gt;Your jobs are naturally idempotent or can be made so.&lt;/li&gt;
&lt;li&gt;1-minute scheduling granularity is acceptable (the common case for notifications, reports, syncs, cleanups, etc.).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Reach for Redis/BullMQ or SQS instead when:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You have hard sub-second or sub-10s scheduling SLAs.&lt;/li&gt;
&lt;li&gt;You need very high fan-out or per-queue throughput beyond current Cloudflare limits.&lt;/li&gt;
&lt;li&gt;You want a true drop-in replacement for an existing heavy BullMQ workload with minimal changes.&lt;/li&gt;
&lt;li&gt;Sandboxed / untrusted processor execution is a hard requirement.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&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; @zintrust/queue-cloudflare
&lt;span class="c"&gt;# or inside a ZinTrust project:&lt;/span&gt;
zin add queue:cloudflare
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The package is small, the surface is explicit, and the docs try very hard not to lie to you.&lt;/p&gt;

&lt;p&gt;If you're doing real work on Cloudflare and queues are still the annoying exception in your architecture diagram, this might be worth an afternoon.&lt;/p&gt;

&lt;p&gt;Feedback, real-world patterns, and "here's where it broke our assumptions" stories are extremely welcome in the repo or Discord.&lt;/p&gt;

&lt;p&gt;The package README and &lt;a href="https://zintrust.com/package-queue-cloudflare" rel="noopener noreferrer"&gt;https://zintrust.com/package-queue-cloudflare&lt;/a&gt; contain the full current guidance.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Package: &lt;a href="https://www.npmjs.com/package/@zintrust/queue-cloudflare" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@zintrust/queue-cloudflare&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Core docs: &lt;a href="https://zintrust.com/package-queue-cloudflare" rel="noopener noreferrer"&gt;https://zintrust.com/package-queue-cloudflare&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The package page includes setup, consumer patterns, wrangler examples, and the documented limitations.&lt;/p&gt;

</description>
      <category>cloudflare</category>
      <category>cloudflarechallenge</category>
      <category>typescript</category>
      <category>redis</category>
    </item>
    <item>
      <title># Stop Flying Blind: Why Your Next Framework Needs a Built-In Debugger (Meet ZinTrust Trace)</title>
      <dc:creator>zintrust Zin</dc:creator>
      <pubDate>Mon, 13 Apr 2026 07:10:58 +0000</pubDate>
      <link>https://dev.to/zintrustjs/-stop-flying-blind-why-your-next-framework-needs-a-built-in-debugger-meet-zintrust-trace-3lo1</link>
      <guid>https://dev.to/zintrustjs/-stop-flying-blind-why-your-next-framework-needs-a-built-in-debugger-meet-zintrust-trace-3lo1</guid>
      <description>&lt;p&gt;Let’s be honest: back-end debugging can sometimes feel like a dark art. We've all been there—sprinkling &lt;code&gt;console.log()&lt;/code&gt; across five different files, hunting through messy terminal output, or trying to stitch together logs from completely separate external services just to figure out why a single API request taking too long.&lt;/p&gt;

&lt;p&gt;It disrupts your flow, kills your productivity, and makes finding application bugs exhausting.&lt;/p&gt;

&lt;p&gt;Enter &lt;strong&gt;ZinTrust Trace&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;ZinTrust Trace is our built-in observability dashboard designed specifically for the ZinTrust framework. It gives you an immediate, beautiful, and deeply integrated view into everything happening inside your application—from database queries and caching to middleware execution and background jobs.&lt;/p&gt;

&lt;p&gt;Looking at the Trace dashboard, you instantly get the full story. Instead of guessing, you can see that a request took 31ms, and exactly how much time was spent in your &lt;code&gt;RateLimit&lt;/code&gt; or &lt;code&gt;XSS&lt;/code&gt; middleware down to the millisecond.&lt;/p&gt;

&lt;p&gt;But why should you care if your MVC framework has an &lt;em&gt;in-built&lt;/em&gt; debugger instead of just plugging in a third-party tool?&lt;/p&gt;

&lt;p&gt;Here are 5 reasons it will completely change the way you code:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Zero Setup, Instant Context
&lt;/h3&gt;

&lt;p&gt;Third-party observability tools are powerful, but they require agents, API keys, complicated configurations, and usually a hit to your budget. With an in-built tool like ZinTrust Trace, you get enterprise-grade visibility right out of the box. Start the server, and immediately see your auth events, exceptions, cache hits, and queries.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. A Truly Holistic View of the Request Lifecycle
&lt;/h3&gt;

&lt;p&gt;External loggers mostly just see raw "text" or isolated network requests. ZinTrust Trace understands your framework's actual architecture. It intuitively knows the difference between a &lt;code&gt;MIDDLEWARE&lt;/code&gt; execution, a background &lt;code&gt;JOB&lt;/code&gt;, a &lt;code&gt;MAIL&lt;/code&gt; dispatch, and an ORM &lt;code&gt;MODEL&lt;/code&gt; event. It groups everything tied to a single request life-cycle, giving you the complete narrative without you having to stitch it together.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Spot Bottlenecks Instantly
&lt;/h3&gt;

&lt;p&gt;Performance issues love to hide in plain sight. Because Trace is natively hooked into the ZinTrust ORM and routing layers, it logs the exact millisecond duration of every step. Whether your &lt;code&gt;auth&lt;/code&gt; middleware took 9ms or a complex SQL query took 100ms, you'll catch sluggish routes and N+1 query problems &lt;em&gt;during&lt;/em&gt; development, long before they affect your users.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Leaner Stack, Fewer Dependencies
&lt;/h3&gt;

&lt;p&gt;Every external tracking agent you bolt onto your app introduces overhead, bloat, and a maintenance tax. Relying on a deeply integrated debugger means your stack stays remarkably lean. Your application runs lighter, and you spend less time wrestling with third-party logging SDKs.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Code and Refactor with Absolute Confidence
&lt;/h3&gt;

&lt;p&gt;When you make a large refactoring change, your automated tests only tell you if the final output is correct. ZinTrust Trace tells you if the &lt;em&gt;runtime behavior&lt;/em&gt; is correct. Did that new service class accidentally trigger 15,000 cache reads? Did a queued job fail silently? With Trace running alongside your dev environment, you get instant behavioral feedback, making you a faster, more confident developer.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Ready to see what your code is really doing?&lt;/strong&gt;&lt;br&gt;
Level up your back-end development experience today with ZinTrust. Clean code, fast execution, and unparalleled visibility—right out of the box.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://zintrust.com" rel="noopener noreferrer"&gt;zintrust.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation:&lt;/strong&gt; &lt;a href="https://zintrust.com/package-trace" rel="noopener noreferrer"&gt;zintrust.com/package-trace&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NPM Package:&lt;/strong&gt; &lt;a href="https://www.npmjs.com/package/@zintrust/trace" rel="noopener noreferrer"&gt;@zintrust/trace&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  ZinTrust #WebDevelopment #Backend #TypeScript #Debugging #MVC #WebDev #SoftwareEngineering #DeveloperTools
&lt;/h1&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Migrate MySQL, Postgres, SQLite, or SQL Server to Cloudflare D1 — with checkpoints and integrity checks</title>
      <dc:creator>zintrust Zin</dc:creator>
      <pubDate>Tue, 17 Mar 2026 07:40:34 +0000</pubDate>
      <link>https://dev.to/zintrustjs/migrate-mysql-postgres-sqlite-or-sql-server-to-cloudflare-d1-with-checkpoints-and-integrity-pkd</link>
      <guid>https://dev.to/zintrustjs/migrate-mysql-postgres-sqlite-or-sql-server-to-cloudflare-d1-with-checkpoints-and-integrity-pkd</guid>
      <description>&lt;p&gt;Moving an existing database to Cloudflare D1 sounds simple until you hit type mismatches, partial failures halfway through a million-row import, or find out afterwards that 3,000 rows silently didn't land.&lt;/p&gt;

&lt;p&gt;We built &lt;strong&gt;&lt;a href="https://www.npmjs.com/package/@zintrust/d1-migrator" rel="noopener noreferrer"&gt;@zintrust/d1-migrator&lt;/a&gt;&lt;/strong&gt; to solve exactly that.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Migrates &lt;strong&gt;MySQL, PostgreSQL, SQLite, or SQL Server → Cloudflare D1&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resumable&lt;/strong&gt; — checkpoints every N rows so a failure doesn't mean starting from zero&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data integrity verified&lt;/strong&gt; — row counts + checksums validated after every batch&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dry-run mode&lt;/strong&gt; — preview the full migration without touching D1&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interactive mode&lt;/strong&gt; — guided prompts for tables with D1 compatibility warnings&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero downtime&lt;/strong&gt; — runs against your live database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TypeScript-first&lt;/strong&gt; — full types, works with &lt;code&gt;@zintrust/core&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Install
&lt;/h2&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; @zintrust/d1-migrator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Quickest possible migration (1-line CLI)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;DB_CONNECTION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mysql &lt;span class="nv"&gt;DB_READ_HOSTS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;127.0.0.1 &lt;span class="nv"&gt;DB_PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3306 &lt;span class="nv"&gt;DB_DATABASE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mydb &lt;span class="nv"&gt;DB_USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;root &lt;span class="nv"&gt;DB_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;secret &lt;span class="nv"&gt;D1_TARGET_DB&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;my-d1-db zin migrate-to-d1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All values can also be passed as explicit CLI flags. The order of precedence is: &lt;strong&gt;CLI flag → env var → default&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  TypeScript API
&lt;/h2&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;D1Migrator&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;@zintrust/d1-migrator&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;progress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;D1Migrator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DataMigrator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;migrateData&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;sourceConnection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mysql://user:password@localhost:3306/mydb&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;sourceDriver&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mysql&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;targetDatabase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;my-d1-db&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;targetType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;d1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;batchSize&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;checkpointInterval&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Migrated: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;processedRows&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; rows`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Type conversions handled automatically
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;th&gt;D1 / SQLite&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DATETIME&lt;/td&gt;
&lt;td&gt;TEXT (ISO 8601)&lt;/td&gt;
&lt;td&gt;Auto-converted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BIGINT&lt;/td&gt;
&lt;td&gt;TEXT&lt;/td&gt;
&lt;td&gt;Precision preserved&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DECIMAL&lt;/td&gt;
&lt;td&gt;TEXT&lt;/td&gt;
&lt;td&gt;Precision preserved&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JSON&lt;/td&gt;
&lt;td&gt;TEXT&lt;/td&gt;
&lt;td&gt;Serialized JSON string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BLOB&lt;/td&gt;
&lt;td&gt;BLOB&lt;/td&gt;
&lt;td&gt;Binary preserved&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Resuming after a failure
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;zin migrate-to-d1 &lt;span class="nt"&gt;--resume&lt;/span&gt; &lt;span class="nt"&gt;--migration-id&lt;/span&gt; &amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Checkpoints are stored in &lt;code&gt;.wrangler/state/v3/migrations/&lt;/code&gt; — only unprocessed rows are re-migrated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;📦 npm: &lt;a href="https://www.npmjs.com/package/@zintrust/d1-migrator" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/@zintrust/d1-migrator&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🐛 Issues: &lt;a href="https://github.com/ZinTrust/zintrust/issues" rel="noopener noreferrer"&gt;https://github.com/ZinTrust/zintrust/issues&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;MIT licensed. Part of the &lt;a href="https://www.npmjs.com/package/@zintrust/core" rel="noopener noreferrer"&gt;ZinTrust&lt;/a&gt; backend framework ecosystem.&lt;/p&gt;

&lt;p&gt;Would love feedback from anyone migrating production databases to D1 — especially edge cases with unusual schemas or large datasets. 👇&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>database</category>
      <category>opensource</category>
      <category>cloudflarechallenge</category>
    </item>
    <item>
      <title>D1 Remote Modes: How Cloudflare D1 Balances Security and DX</title>
      <dc:creator>zintrust Zin</dc:creator>
      <pubDate>Mon, 02 Mar 2026 15:49:38 +0000</pubDate>
      <link>https://dev.to/zintrustjs/d1-remote-modes-how-cloudflare-d1-balances-security-and-dx-4nga</link>
      <guid>https://dev.to/zintrustjs/d1-remote-modes-how-cloudflare-d1-balances-security-and-dx-4nga</guid>
      <description>&lt;p&gt;Ever wondered how to connect securely to Cloudflare D1 while maintaining a good developer experience? The &lt;code&gt;D1RemoteAdapter&lt;/code&gt; implements a clever dual-mode approach that automatically adapts to your environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛡️ Registry Mode: Production Security
&lt;/h2&gt;

&lt;p&gt;In production, the adapter uses &lt;strong&gt;registry mode&lt;/strong&gt; - a security-first approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// No raw SQL ever leaves your application&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;d1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SELECT * FROM users&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Becomes: POST /zin/d1/statement { statementId: "abc123...", params: [] }&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The system hashes your SQL and looks it up in a pre-approved allowlist. This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Zero SQL Injection Risk&lt;/strong&gt; at the network level&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Maximum Security&lt;/strong&gt; even with compromised credentials
&lt;/li&gt;
&lt;li&gt;❌ &lt;strong&gt;Higher friction&lt;/strong&gt; for query changes&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🔧 SQL Mode: Developer Experience
&lt;/h2&gt;

&lt;p&gt;In development, you get &lt;strong&gt;SQL mode&lt;/strong&gt; for maximum flexibility:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Raw SQL sent directly&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;d1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SELECT * FROM users WHERE id = ?&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;userId&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="c1"&gt;// Becomes: POST /zin/d1/query "SELECT * FROM users WHERE id = ?"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Benefits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ &lt;strong&gt;Zero friction&lt;/strong&gt; - write code, run immediately&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Migration friendly&lt;/strong&gt; - dynamic SQL works perfectly&lt;/li&gt;
&lt;li&gt;✅ &lt;strong&gt;Rapid iteration&lt;/strong&gt; - no configuration needed&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🎯 Smart Auto-Detection
&lt;/h2&gt;

&lt;p&gt;The adapter automatically chooses the right mode:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Environment&lt;/th&gt;
&lt;th&gt;Mode&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Production&lt;/td&gt;
&lt;td&gt;Registry&lt;/td&gt;
&lt;td&gt;Security first&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Development&lt;/td&gt;
&lt;td&gt;SQL&lt;/td&gt;
&lt;td&gt;DX first&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test&lt;/td&gt;
&lt;td&gt;SQL&lt;/td&gt;
&lt;td&gt;Flexibility first&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  🚀 Why This Design Matters
&lt;/h2&gt;

&lt;p&gt;This dual-mode approach solves a real problem: production apps need security, dev apps need flexibility. Instead of forcing you to choose one or the other, you get both automatically.&lt;/p&gt;

&lt;p&gt;The registry mode's allowlist system is particularly clever - it prevents network-level SQL injection while still allowing your application to work normally. Even if attackers steal your API keys, they're limited to queries you've explicitly approved.&lt;/p&gt;

&lt;p&gt;For anyone building production apps with D1, this pattern provides enterprise-grade security without sacrificing developer productivity.&lt;/p&gt;

&lt;h1&gt;
  
  
  Cloudflare #D1 #DatabaseSecurity #NodeJS #TypeScript #WebDev
&lt;/h1&gt;

</description>
      <category>webdev</category>
      <category>cloudflarechallenge</category>
      <category>typescript</category>
      <category>ai</category>
    </item>
    <item>
      <title>ZinTrust: a TypeScript backend framework for structure without decorators</title>
      <dc:creator>zintrust Zin</dc:creator>
      <pubDate>Wed, 07 Jan 2026 08:22:44 +0000</pubDate>
      <link>https://dev.to/zintrustjs/zintrust-a-typescript-backend-framework-for-structure-without-decorators-198n</link>
      <guid>https://dev.to/zintrustjs/zintrust-a-typescript-backend-framework-for-structure-without-decorators-198n</guid>
      <description>&lt;h2&gt;
  
  
  ZinTrust: structure without the ceremony (and without decorators)
&lt;/h2&gt;

&lt;p&gt;If you’ve built TypeScript APIs for a while, you’ve probably felt the pull in two directions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Super flexible&lt;/strong&gt; stacks where structure can drift over time.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Highly opinionated&lt;/strong&gt; frameworks where decorators/DI patterns can feel heavy (depending on your team).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wanted a middle ground—clear structure, explicit wiring, and a &lt;strong&gt;minimal core&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So I’ve been building &lt;strong&gt;ZinTrust&lt;/strong&gt;: a production-grade TypeScript backend framework with &lt;strong&gt;no Express/Fastify as the core&lt;/strong&gt;, plus adapters you add when you need them.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docs: &lt;a href="https://zintrust.com" rel="noopener noreferrer"&gt;https://zintrust.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/ZinTrust/ZinTrust" rel="noopener noreferrer"&gt;https://github.com/ZinTrust/ZinTrust&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;X: &lt;a href="https://x.com/zintrustjs" rel="noopener noreferrer"&gt;https://x.com/zintrustjs&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What “minimal core” means (in practice)
&lt;/h2&gt;

&lt;p&gt;For me it’s about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Explicit wiring&lt;/strong&gt; you can trace when debugging.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composability&lt;/strong&gt; (add what you need; avoid pulling in the world by default).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type-first ergonomics&lt;/strong&gt; without leaning on magic.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A tiny routing example
&lt;/h2&gt;

&lt;p&gt;Here’s the style of routing / grouping:&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;Router&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;IRouter&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;@zintrust/core&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;function&lt;/span&gt; &lt;span class="nf"&gt;registerRoutes&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="nx"&gt;IRouter&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="k"&gt;void&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="nf"&gt;group&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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/v1&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;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="nf"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/users&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;index&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;show&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;destroy&lt;/span&gt; &lt;span class="p"&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;h2&gt;
  
  
  Quickstart
&lt;/h2&gt;

&lt;p&gt;If you want to kick the tires quickly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-g&lt;/span&gt; @zintrust/core
zin new my-app
&lt;span class="nb"&gt;cd &lt;/span&gt;my-app
zin start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The feedback I want (seriously)
&lt;/h2&gt;

&lt;p&gt;This is the phase where &lt;strong&gt;docs and onboarding&lt;/strong&gt; matter more than “features”. If you try it, I’d love blunt feedback on:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Where do you get stuck in the quickstart?&lt;/li&gt;
&lt;li&gt;Which concepts feel unclear (routing, structure, ORM, config, etc.)?&lt;/li&gt;
&lt;li&gt;What would make you confident this is production-ready?&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Reply here, open an issue, or join the community:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Discord: &lt;a href="https://discord.gg/sEU2brHMQd" rel="noopener noreferrer"&gt;https://discord.gg/sEU2brHMQd&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Slack: &lt;a href="https://join.slack.com/t/zintrustjs/shared_invite/zt-3ljhlzn3n-H7jsI1n1bTXKYRu2FSRGag" rel="noopener noreferrer"&gt;https://join.slack.com/t/zintrustjs/shared_invite/zt-3ljhlzn3n-H7jsI1n1bTXKYRu2FSRGag&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  If you only look at one thing
&lt;/h2&gt;

&lt;p&gt;If you only have 2 minutes, check the docs and tell me what’s confusing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://zintrust.com" rel="noopener noreferrer"&gt;https://zintrust.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading—and if you try it, thank you even more for the critique.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>node</category>
      <category>backend</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
