<?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: Shayan M Hussain</title>
    <description>The latest articles on DEV Community by Shayan M Hussain (@shayanhussainsb).</description>
    <link>https://dev.to/shayanhussainsb</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3733965%2Feaca42d7-ed4a-4599-a03c-9df22103c50e.jpg</url>
      <title>DEV Community: Shayan M Hussain</title>
      <link>https://dev.to/shayanhussainsb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shayanhussainsb"/>
    <language>en</language>
    <item>
      <title>Run Express on Bun. Without the rewrite.</title>
      <dc:creator>Shayan M Hussain</dc:creator>
      <pubDate>Wed, 11 Mar 2026 02:24:18 +0000</pubDate>
      <link>https://dev.to/shayanhussainsb/run-express-on-bun-without-the-rewrite-d6k</link>
      <guid>https://dev.to/shayanhussainsb/run-express-on-bun-without-the-rewrite-d6k</guid>
      <description>&lt;h2&gt;
  
  
  bunWay: Express Feel, Bun-Ready, 19 Built-In Middleware
&lt;/h2&gt;

&lt;p&gt;Most teams don't avoid Bun because they dislike performance.&lt;/p&gt;

&lt;p&gt;They avoid Bun because migration feels expensive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;new framework mental model,&lt;/li&gt;
&lt;li&gt;rewritten middleware stack,&lt;/li&gt;
&lt;li&gt;route-by-route uncertainty,&lt;/li&gt;
&lt;li&gt;team retraining.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;bunWay&lt;/strong&gt; exists to remove that friction.&lt;/p&gt;

&lt;p&gt;It gives you a Bun-native framework with the &lt;strong&gt;Express-style developer experience&lt;/strong&gt; your team already knows.&lt;/p&gt;

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

&lt;p&gt;bunWay is a web framework for Bun with strong Express API compatibility:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;same &lt;code&gt;(req, res, next)&lt;/code&gt; handler style,&lt;/li&gt;
&lt;li&gt;familiar routing patterns,&lt;/li&gt;
&lt;li&gt;familiar middleware flow,&lt;/li&gt;
&lt;li&gt;one package with &lt;strong&gt;19 built-in middleware&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is not about inventing a new style. It's about helping Express teams adopt Bun without feeling like they have to start from zero.&lt;/p&gt;




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

&lt;p&gt;At JointOps, we kept seeing the same blocker:&lt;/p&gt;

&lt;p&gt;"We want Bun, but we don't want to rewrite everything right now."&lt;/p&gt;

&lt;p&gt;So we built bunWay for real migration constraints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;existing Express codebases,&lt;/li&gt;
&lt;li&gt;business deadlines,&lt;/li&gt;
&lt;li&gt;teams that need predictable rollouts.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Who bunWay is for
&lt;/h2&gt;

&lt;p&gt;You should look at bunWay if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;your team already ships Express APIs,&lt;/li&gt;
&lt;li&gt;you want to adopt Bun incrementally,&lt;/li&gt;
&lt;li&gt;you want fewer moving parts in your middleware stack,&lt;/li&gt;
&lt;li&gt;you want a familiar developer experience instead of a full framework reset.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You should validate first if:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;you depend on niche Express internals,&lt;/li&gt;
&lt;li&gt;you need strict Node.js runtime compatibility,&lt;/li&gt;
&lt;li&gt;your app relies on uncommon behavior paths you haven't tested yet.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What you get out of the box
&lt;/h2&gt;

&lt;p&gt;bunWay ships with &lt;strong&gt;19 built-in middleware&lt;/strong&gt;, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;body parsing (&lt;code&gt;json&lt;/code&gt;, &lt;code&gt;urlencoded&lt;/code&gt;, &lt;code&gt;text&lt;/code&gt;, &lt;code&gt;raw&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;security (&lt;code&gt;helmet&lt;/code&gt;, &lt;code&gt;csrf&lt;/code&gt;, &lt;code&gt;hpp&lt;/code&gt;, &lt;code&gt;rateLimit&lt;/code&gt;, &lt;code&gt;cors&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;auth/session (&lt;code&gt;session&lt;/code&gt;, &lt;code&gt;passport&lt;/code&gt;, &lt;code&gt;cookieParser&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;files/static (&lt;code&gt;upload&lt;/code&gt;, &lt;code&gt;serveStatic&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;utilities (&lt;code&gt;logger&lt;/code&gt;, &lt;code&gt;compression&lt;/code&gt;, &lt;code&gt;timeout&lt;/code&gt;, &lt;code&gt;validate&lt;/code&gt;, &lt;code&gt;errorHandler&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The point is simple: common API needs in one framework package.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quick look
&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="nx"&gt;bunway&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;helmet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;logger&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;bunway&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;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bunway&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bunway&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;cors&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;helmet&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dev&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="nx"&gt;app&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;/health&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;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;ok&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="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you already know Express, this should feel immediately familiar.&lt;/p&gt;




&lt;h2&gt;
  
  
  Migration mindset (short version)
&lt;/h2&gt;

&lt;p&gt;bunWay is best used as an incremental migration path:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;start with imports,&lt;/li&gt;
&lt;li&gt;move route groups gradually,&lt;/li&gt;
&lt;li&gt;validate behavior with tests,&lt;/li&gt;
&lt;li&gt;keep shipping.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No forced rewrite sprint.&lt;/p&gt;




&lt;h2&gt;
  
  
  Final thought
&lt;/h2&gt;

&lt;p&gt;bunWay is not a "look how fast" product.&lt;/p&gt;

&lt;p&gt;It's a &lt;strong&gt;"remove excuses to try Bun"&lt;/strong&gt; product for teams that value delivery confidence and familiar patterns.&lt;/p&gt;

&lt;p&gt;If that sounds like your team, start small and evaluate it in one service. Please leave a star on repo ⭐️&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docs: &lt;a href="https://bunway.jointops.dev" rel="noopener noreferrer"&gt;https://bunway.jointops.dev&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/JointOps/bunway" rel="noopener noreferrer"&gt;https://github.com/JointOps/bunway&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;npm: &lt;a href="https://www.npmjs.com/package/bunway" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/bunway&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Discord: &lt;a href="https://discord.gg/fTF4qjaMFT" rel="noopener noreferrer"&gt;https://discord.gg/fTF4qjaMFT&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bun add bunway
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;em&gt;Built by the JointOps team. MIT licensed. Contributions welcome.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>express</category>
      <category>opensource</category>
      <category>typescript</category>
      <category>bunjs</category>
    </item>
    <item>
      <title>Redis Changed Its License. Here's How I Future-Proofed My Rate Limiter with Valkey and DragonflyDB.</title>
      <dc:creator>Shayan M Hussain</dc:creator>
      <pubDate>Thu, 26 Feb 2026 01:50:21 +0000</pubDate>
      <link>https://dev.to/shayanhussainsb/redis-changed-its-license-heres-how-i-future-proofed-my-rate-limiter-with-valkey-and-dragonflydb-4484</link>
      <guid>https://dev.to/shayanhussainsb/redis-changed-its-license-heres-how-i-future-proofed-my-rate-limiter-with-valkey-and-dragonflydb-4484</guid>
      <description>&lt;p&gt;If you're running a Node.js or Bun API in production, there's a good chance Redis is somewhere in your stack. And if you've been paying attention to the open source world over the past two years, you know that "just use Redis" isn't as simple as it used to be.&lt;/p&gt;

&lt;p&gt;I maintain &lt;a href="https://github.com/JointOps/hitlimit-monorepo" rel="noopener noreferrer"&gt;hitlimit&lt;/a&gt;, a rate limiting library for Node.js and Bun. This week I shipped v1.3.0 with first-class Valkey and DragonflyDB support. Here's why, and what the numbers look like.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Redis Licensing Timeline
&lt;/h2&gt;

&lt;p&gt;For 15 years, Redis was BSD-licensed. Use it however you want, no restrictions. Then things changed fast:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;March 2024&lt;/strong&gt; — Redis switches from BSD to dual SSPL/RSAL. The goal: stop cloud providers (AWS, Google) from offering managed Redis without paying. The community reaction is immediate and hostile.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;March 2024&lt;/strong&gt; — The Linux Foundation announces &lt;a href="https://valkey.io/" rel="noopener noreferrer"&gt;Valkey&lt;/a&gt;, a fork of Redis 7.2.4 under the BSD license. AWS, Google, Oracle, Ericsson, and Snap back it on day one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;April 2025&lt;/strong&gt; — One year later, &lt;a href="https://devclass.com/2025/04/01/one-year-ago-redis-changed-its-license-and-lost-most-of-its-external-contributors/" rel="noopener noreferrer"&gt;DevClass reports&lt;/a&gt; that Redis has lost most of its external contributors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;May 2025&lt;/strong&gt; — Redis 8 adds AGPLv3 as a third license option. Antirez (the original creator) returns to help course-correct.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;September 2024&lt;/strong&gt; — Percona's survey reports 83% of large enterprises have adopted or are actively exploring Valkey. Over 70% of Redis users cite the licensing change as motivation to seek alternatives.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2026&lt;/strong&gt; — Valkey reaches v9 with features Redis doesn't have. DragonflyDB, a high-performance Redis-compatible store, continues gaining ground.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AGPL option is an improvement, but the trust damage was done. Valkey is at v9 with a thriving contributor base, and many teams aren't looking back.&lt;/p&gt;

&lt;h2&gt;
  
  
  What This Means for Your API
&lt;/h2&gt;

&lt;p&gt;If your rate limiting relies on Redis, you now have licensing questions. Maybe they don't affect you today — AGPL might be fine for your use case. But if you're at a company with a legal team, or you ship software to customers, or you simply prefer not to think about it, Valkey and DragonflyDB eliminate those questions entirely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Valkey&lt;/strong&gt;: Linux Foundation project. BSD licensed. Same Redis protocol, same commands, same client libraries. Drop-in replacement.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DragonflyDB&lt;/strong&gt;: Redis-compatible, optimized for multi-core. BSL licensed. Designed for high-throughput workloads.&lt;/p&gt;

&lt;p&gt;Both work with ioredis. Both run the same Lua scripts. Both use the same port. The migration is boring — which is exactly what you want from infrastructure changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Migration: Three Lines of Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before — Redis&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;redisStore&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;@joint-ops/hitlimit/stores/redis&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;redisStore&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="s1"&gt;redis://localhost:6379&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// After — Valkey (literally change the import)&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;valkeyStore&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;@joint-ops/hitlimit/stores/valkey&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;valkeyStore&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="s1"&gt;redis://localhost:6379&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;Under the hood, &lt;code&gt;ValkeyStore&lt;/code&gt; and &lt;code&gt;DragonflyStore&lt;/code&gt; run the exact same Lua pipeline as &lt;code&gt;RedisStore&lt;/code&gt;. Same atomic INCR + conditional PEXPIRE in a single round-trip. Zero new logic, zero new failure modes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- The Lua script powering all three stores&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KEYS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;windowMs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;tonumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ARGV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'INCR'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;ttl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PTTL'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ttl&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
  &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'PEXPIRE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;windowMs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;ttl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;windowMs&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Benchmarks
&lt;/h2&gt;

&lt;p&gt;I benchmark every store, every framework, every release. Here are the v1.3.0 distributed store numbers — the ones that actually matter when you're running multiple API instances behind a load balancer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test methodology&lt;/strong&gt;: 50,000 iterations × 5 runs per scenario, 5,000 warmup iterations discarded. Node.js v24.4.1, Apple M1. All raw JSON results are in the &lt;a href="https://github.com/JointOps/hitlimit-monorepo/tree/main/benchmarks" rel="noopener noreferrer"&gt;benchmarks directory&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Redis vs Valkey vs DragonflyDB
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Store&lt;/th&gt;
&lt;th&gt;ops/sec&lt;/th&gt;
&lt;th&gt;Avg Latency&lt;/th&gt;
&lt;th&gt;p99 Latency&lt;/th&gt;
&lt;th&gt;License&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Valkey&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6,879&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;145μs&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;250μs&lt;/td&gt;
&lt;td&gt;BSD&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redis&lt;/td&gt;
&lt;td&gt;6,733&lt;/td&gt;
&lt;td&gt;164μs&lt;/td&gt;
&lt;td&gt;266μs&lt;/td&gt;
&lt;td&gt;RSAL/SSPL/AGPL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DragonflyDB&lt;/td&gt;
&lt;td&gt;5,861&lt;/td&gt;
&lt;td&gt;170μs&lt;/td&gt;
&lt;td&gt;272μs&lt;/td&gt;
&lt;td&gt;BSL&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Postgres&lt;/td&gt;
&lt;td&gt;3,491&lt;/td&gt;
&lt;td&gt;286μs&lt;/td&gt;
&lt;td&gt;608μs&lt;/td&gt;
&lt;td&gt;PostgreSQL&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Valkey and Redis are within measurement noise. Same protocol, same Lua scripts, same ioredis client underneath. The bottleneck is network round-trip time — ~150μs over localhost — not the datastore.&lt;/p&gt;

&lt;p&gt;DragonflyDB is slightly slower for this workload because rate limiting uses simple single-key operations where Dragonfly's multi-core architecture doesn't kick in. Still well within production tolerance.&lt;/p&gt;

&lt;p&gt;Postgres is roughly half the throughput — but 3,500 ops/sec is still far more than most APIs need, and if you're already running PG, it means zero extra infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  With Framework Middleware (Node.js)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Framework&lt;/th&gt;
&lt;th&gt;Store&lt;/th&gt;
&lt;th&gt;ops/sec&lt;/th&gt;
&lt;th&gt;Avg Latency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Express&lt;/td&gt;
&lt;td&gt;Redis&lt;/td&gt;
&lt;td&gt;6,188&lt;/td&gt;
&lt;td&gt;161μs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Express&lt;/td&gt;
&lt;td&gt;Valkey&lt;/td&gt;
&lt;td&gt;~6,200&lt;/td&gt;
&lt;td&gt;~160μs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hono&lt;/td&gt;
&lt;td&gt;Redis&lt;/td&gt;
&lt;td&gt;6,340&lt;/td&gt;
&lt;td&gt;157μs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fastify&lt;/td&gt;
&lt;td&gt;Redis&lt;/td&gt;
&lt;td&gt;5,272&lt;/td&gt;
&lt;td&gt;189μs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Framework overhead is negligible when each operation spends ~150μs on a network round-trip. The store choice matters more than the framework choice at this layer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Bun Distributed Performance
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Store&lt;/th&gt;
&lt;th&gt;ops/sec&lt;/th&gt;
&lt;th&gt;Avg Latency&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Valkey&lt;/td&gt;
&lt;td&gt;6,075&lt;/td&gt;
&lt;td&gt;164μs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redis&lt;/td&gt;
&lt;td&gt;6,984&lt;/td&gt;
&lt;td&gt;143μs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DragonflyDB&lt;/td&gt;
&lt;td&gt;6,008&lt;/td&gt;
&lt;td&gt;166μs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Same story on Bun — all three stores are within noise for rate limiting workloads.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing the Right Store
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Store&lt;/th&gt;
&lt;th&gt;ops/sec&lt;/th&gt;
&lt;th&gt;Distributed?&lt;/th&gt;
&lt;th&gt;License&lt;/th&gt;
&lt;th&gt;Best for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Valkey&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6,879&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;BSD&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Multi-instance, no licensing questions&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Redis&lt;/td&gt;
&lt;td&gt;6,733&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;RSAL/SSPL/AGPL&lt;/td&gt;
&lt;td&gt;Multi-instance, if AGPL works for you&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DragonflyDB&lt;/td&gt;
&lt;td&gt;5,861&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;BSL&lt;/td&gt;
&lt;td&gt;Multi-instance, multi-core optimized&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Postgres&lt;/td&gt;
&lt;td&gt;3,491&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;PostgreSQL&lt;/td&gt;
&lt;td&gt;Already running PG, no extra infra&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SQLite&lt;/td&gt;
&lt;td&gt;418,260&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Public domain&lt;/td&gt;
&lt;td&gt;Single instance, survives restarts&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Full Example
&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="nx"&gt;express&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;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createLimiter&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;@joint-ops/hitlimit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;valkeyStore&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;@joint-ops/hitlimit/stores/valkey&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;express&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;limiter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createLimiter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;valkeyStore&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="s1"&gt;redis://localhost:6379&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;windowMs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// 15 minutes&lt;/span&gt;
  &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// Apply to all routes&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;limiter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="c1"&gt;// Or different limits per route&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;strictLimiter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createLimiter&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;valkeyStore&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="s1"&gt;redis://localhost:6379&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="na"&gt;windowMs&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;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&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/login&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;strictLimiter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;express&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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="c1"&gt;// 10 attempts per minute&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;app&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="s1"&gt;/api/data&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;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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="c1"&gt;// 100 requests per 15 minutes (from the global limiter)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Works with Express, Fastify, Hono, NestJS, Bun.serve, and Elysia. Same API across all frameworks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Dedicated Stores Instead of "Just Use the Redis Store"?
&lt;/h2&gt;

&lt;p&gt;You &lt;em&gt;can&lt;/em&gt; use the Redis store with Valkey and DragonflyDB — it works perfectly. But dedicated stores give you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Clarity&lt;/strong&gt; — &lt;code&gt;valkeyStore()&lt;/code&gt; is more descriptive than &lt;code&gt;redisStore()&lt;/code&gt; pointed at a Valkey instance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Discoverability&lt;/strong&gt; — someone searching "valkey rate limiter" on npm actually finds it&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Future-proofing&lt;/strong&gt; — if Valkey or DragonflyDB ever diverge from Redis protocol, we have a clean abstraction point&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Getting Started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Node.js&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; @joint-ops/hitlimit

&lt;span class="c"&gt;# Bun&lt;/span&gt;
bun add @joint-ops/hitlimit-bun
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;~8KB core bundle (Node.js) / ~18KB (Bun). Tree-shakeable stores — you only pay for what you import. Zero runtime dependencies. MIT licensed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt;: &lt;a href="https://github.com/JointOps/hitlimit-monorepo" rel="noopener noreferrer"&gt;https://github.com/JointOps/hitlimit-monorepo&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Has the Redis licensing situation changed any of your infrastructure decisions? I'd love to hear about it in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>redis</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Six frameworks. Four storage backends. One import. Zero dependencies.</title>
      <dc:creator>Shayan M Hussain</dc:creator>
      <pubDate>Sun, 22 Feb 2026 05:31:58 +0000</pubDate>
      <link>https://dev.to/shayanhussainsb/six-frameworks-four-storage-backends-one-import-zero-dependencies-2k1o</link>
      <guid>https://dev.to/shayanhussainsb/six-frameworks-four-storage-backends-one-import-zero-dependencies-2k1o</guid>
      <description>&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;hitlimit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;window&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1m&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;Same API everywhere. Express, Fastify, Hono, NestJS, Bun.serve, Elysia. No adapters to install. No framework-specific config. Just one package and you are done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Every Framework, Same Code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Express&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;hitlimit&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;@joint-ops/hitlimit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;hitlimit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;window&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1m&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}))&lt;/span&gt;

&lt;span class="c1"&gt;// Fastify&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;hitlimit&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;@joint-ops/hitlimit/fastify&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hitlimit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;window&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1m&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// Hono&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;hitlimit&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;@joint-ops/hitlimit/hono&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;hitlimit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;window&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1m&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}))&lt;/span&gt;

&lt;span class="c1"&gt;// NestJS&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HitLimitModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;HitLimitGuard&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;@joint-ops/hitlimit/nest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Module&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;HitLimitModule&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;window&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1m&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})],&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;APP_GUARD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;useClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HitLimitGuard&lt;/span&gt; &lt;span class="p"&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;class&lt;/span&gt; &lt;span class="nc"&gt;AppModule&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="c1"&gt;// Bun.serve&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;hitlimit&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;@joint-ops/hitlimit-bun&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="nx"&gt;Bun&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;serve&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;hitlimit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;window&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1m&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// Elysia&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;hitlimit&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;@joint-ops/hitlimit-bun/elysia&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Elysia&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;hitlimit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;window&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1m&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})).&lt;/span&gt;&lt;span class="nf"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Switch frameworks tomorrow. Your rate limiting code stays the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  4 Storage Backends Built In
&lt;/h2&gt;

&lt;p&gt;No extra packages. No separate installs. Pick the right store for your deployment and swap it in one line.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Memory (default) - fastest, no setup&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;hitlimit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;window&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1m&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}))&lt;/span&gt;

&lt;span class="c1"&gt;// SQLite - survives restarts&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;hitlimit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;sqliteStore&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./limits.db&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="c1"&gt;// Redis - distributed across instances&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;hitlimit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;redisStore&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="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_URL&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="c1"&gt;// Postgres - use your existing database&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;hitlimit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;postgresStore&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;pool&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;p&gt;Start with memory on day one. Move to Postgres or Redis when you scale. Nothing else changes.&lt;/p&gt;

&lt;h2&gt;
  
  
  New in v1.2.0: PostgreSQL Store
&lt;/h2&gt;

&lt;p&gt;Most teams already have Postgres running. Why add Redis just for rate limiting?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&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;postgresStore&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;@joint-ops/hitlimit/stores/postgres&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Pool&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;pg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Pool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;connectionString&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;DATABASE_URL&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;hitlimit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;window&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1m&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;postgresStore&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One atomic query per request. Zero race conditions. Tables created automatically on first run. Named prepared statements for 30 to 40 percent lower latency. No new infrastructure to manage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fast
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Node.js (10K unique IPs)
Memory       3.16M ops/s      316ns
SQLite       352K ops/s       2.8us
Redis        6.7K ops/s       149us
Postgres     3.0K ops/s       336us

Bun (10K unique IPs)
Memory       8.32M ops/s      120ns
bun:sqlite   325K ops/s       3.1us
Redis        6.7K ops/s       148us
Postgres     3.7K ops/s       273us
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Peak: &lt;strong&gt;12.38M ops/s&lt;/strong&gt; on Bun. &lt;strong&gt;4.83M ops/s&lt;/strong&gt; on Node.js.&lt;/p&gt;

&lt;p&gt;All benchmarks are &lt;a href="https://github.com/JointOps/hitlimit-monorepo/tree/main/benchmarks" rel="noopener noreferrer"&gt;open source&lt;/a&gt;. Clone the repo and run them on your hardware.&lt;/p&gt;

&lt;h2&gt;
  
  
  More Than a Counter
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Tiered limits&lt;/strong&gt; for free, pro and enterprise plans.&lt;br&gt;
&lt;strong&gt;Auto ban&lt;/strong&gt; repeat offenders after N violations.&lt;br&gt;
&lt;strong&gt;Group limits&lt;/strong&gt; for per-team or per-org quotas.&lt;br&gt;
&lt;strong&gt;Skip rules&lt;/strong&gt; for health checks, admins, internal routes.&lt;br&gt;
&lt;strong&gt;Human readable windows&lt;/strong&gt; like '15m', '1h', '1d' instead of milliseconds.&lt;/p&gt;

&lt;p&gt;All built in. All zero dependencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get Started
&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; @joint-ops/hitlimit    &lt;span class="c"&gt;# Node.js&lt;/span&gt;
bun add @joint-ops/hitlimit-bun    &lt;span class="c"&gt;# Bun&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://hitlimit.jointops.dev" rel="noopener noreferrer"&gt;Docs&lt;/a&gt; | &lt;a href="https://www.npmjs.com/package/@joint-ops/hitlimit" rel="noopener noreferrer"&gt;npm&lt;/a&gt; | &lt;a href="https://github.com/JointOps/hitlimit-monorepo/releases/tag/v1.2.0" rel="noopener noreferrer"&gt;Release notes&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If it saves you time give it a star.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>node</category>
      <category>bunjs</category>
      <category>webdev</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Shayan M Hussain</dc:creator>
      <pubDate>Mon, 02 Feb 2026 17:55:09 +0000</pubDate>
      <link>https://dev.to/shayanhussainsb/-114b</link>
      <guid>https://dev.to/shayanhussainsb/-114b</guid>
      <description>&lt;div class="ltag__link"&gt;
  &lt;a href="/builtbyali" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3742497%2Fecc3a3c6-8393-4e0a-9595-be54b26b6136.jpeg" alt="builtbyali"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/builtbyali/building-a-rest-api-with-bunway-in-10-minutes-596b" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Building a REST API with bunWay in 10 Minutes&lt;/h2&gt;
      &lt;h3&gt;Ali ・ Feb 2&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#bunjs&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#api&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#tutorial&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#beginners&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


</description>
      <category>bunjs</category>
      <category>api</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
    <item>
      <title>One rate limiter for Express, NestJS, and Bun published</title>
      <dc:creator>Shayan M Hussain</dc:creator>
      <pubDate>Tue, 27 Jan 2026 03:01:45 +0000</pubDate>
      <link>https://dev.to/shayanhussainsb/i-built-a-rate-limiter-thats-9x-faster-than-rate-limiter-flexible-benchmarks-included-3409</link>
      <guid>https://dev.to/shayanhussainsb/i-built-a-rate-limiter-thats-9x-faster-than-rate-limiter-flexible-benchmarks-included-3409</guid>
      <description>&lt;p&gt;I got tired of installing different rate limiting packages for every framework.&lt;/p&gt;

&lt;p&gt;Express? One package. NestJS? Different package. Bun? Good luck finding one.&lt;/p&gt;

&lt;p&gt;So I built one that works everywhere:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;hitlimit&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;window&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;15m&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;Same config. Express, NestJS, Bun, Elysia — doesn't matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's in it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;All the stores&lt;/strong&gt; — Memory, SQLite, Redis&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Tiered limits&lt;/strong&gt; — Free/Pro/Enterprise built in, no manual routing&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Human time windows&lt;/strong&gt; — &lt;code&gt;'15m'&lt;/code&gt; not &lt;code&gt;900000&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lightweight&lt;/strong&gt; — ~7KB&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;p&gt;I ran benchmarks. It's fast — competitive with or faster than the alternatives depending on the scenario. But realistically, rate limiting isn't your bottleneck unless you're at serious scale.&lt;/p&gt;

&lt;p&gt;Benchmark scripts are in the repo if you want to verify.&lt;/p&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; @joint-ops/hitlimit

&lt;span class="c"&gt;# Bun&lt;/span&gt;
bun add @joint-ops/hitlimit-bun
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/JointOps/hitlimit-monorepo" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; · &lt;a href="https://hitlimit.jointops.dev" rel="noopener noreferrer"&gt;Docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's brand new. Would love feedback — what's missing? What would make this useful for your projects?&lt;/p&gt;

</description>
      <category>programming</category>
      <category>node</category>
      <category>npm</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
