<?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>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>
