@jaredwray/mockhttp 2.1.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -35,7 +35,7 @@
35
35
  </script>
36
36
  <title>MockHTTP - Bins</title>
37
37
  <script type="application/ld+json">
38
- {"@context":"https://schema.org","@type":"TechArticle","headline":"Bins","description":"","url":"https://mockhttp.org/docs/bins/","publisher":{"@type":"Organization","name":"MockHTTP"},"dateModified":"2026-10-03","keywords":[]}
38
+ {"@context":"https://schema.org","@type":"TechArticle","headline":"Bins","description":"","url":"https://mockhttp.org/docs/bins/","publisher":{"@type":"Organization","name":"MockHTTP"},"dateModified":"2026-10-20","keywords":[]}
39
39
  </script>
40
40
  </head>
41
41
 
@@ -330,7 +330,7 @@ here is stored against bin <code>:id</code></li>
330
330
  <li><a href="#configuration">Configuration</a>
331
331
  <ul>
332
332
  <li><a href="#defaults">Defaults</a></li>
333
- <li><a href="#pluggable-storage">Pluggable Storage</a></li>
333
+ <li><a href="#storage">Storage</a></li>
334
334
  </ul>
335
335
  </li>
336
336
  </ul>
@@ -514,11 +514,11 @@ curl -s <span class="hljs-string">"http://localhost:3000/bins/<span class="hljs-
514
514
  | jq -r <span class="hljs-string">'.requests[0].body'</span> | <span class="hljs-built_in">base64</span> -d > recovered.png
515
515
  </code></pre>
516
516
  <h3 id="truncation">Truncation</h3>
517
- <p>Bodies larger than <code>maxBodySize</code> (1 MiB by default) are cut off and flagged:</p>
517
+ <p>Bodies larger than <code>maxBodySize</code> (128 KiB by default) are cut off and flagged:</p>
518
518
  <pre><code class="hljs language-javascript"><span class="hljs-comment">// In test setup</span>
519
- <span class="hljs-keyword">import</span> { <span class="hljs-title class_">MockHttp</span>, <span class="hljs-title class_">BinManager</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'@jaredwray/mockhttp'</span>;
519
+ <span class="hljs-keyword">import</span> { <span class="hljs-title class_">MockHttp</span>, <span class="hljs-title class_">MockHttpBins</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'@jaredwray/mockhttp'</span>;
520
520
  <span class="hljs-keyword">const</span> mock = <span class="hljs-keyword">new</span> <span class="hljs-title class_">MockHttp</span>();
521
- mock.<span class="hljs-property">bins</span> = <span class="hljs-keyword">new</span> <span class="hljs-title class_">BinManager</span>({ <span class="hljs-attr">maxBodySize</span>: <span class="hljs-number">1024</span> }); <span class="hljs-comment">// 1 KiB cap</span>
521
+ mock.<span class="hljs-property">bins</span> = <span class="hljs-keyword">new</span> <span class="hljs-title class_">MockHttpBins</span>({ <span class="hljs-attr">maxBodySize</span>: <span class="hljs-number">1024</span> }); <span class="hljs-comment">// 1 KiB cap</span>
522
522
  <span class="hljs-keyword">await</span> mock.<span class="hljs-title function_">start</span>();
523
523
  </code></pre>
524
524
  <pre><code class="hljs language-bash"><span class="hljs-comment"># Send 5000 bytes to a bin with a 1 KiB cap</span>
@@ -550,21 +550,20 @@ curl -s <span class="hljs-string">"http://localhost:3000/bins/<span class="hljs-
550
550
  <span class="hljs-comment"># "/event/105"</span>
551
551
  </code></pre>
552
552
  <h2 id="programmatic-access">Programmatic Access</h2>
553
- <p>The bin manager is exposed on the <code>MockHttp</code> instance as <code>mock.bins</code>. This
554
- makes it easy to drive bins from a test suite without going through HTTP:</p>
553
+ <p>Use <code>mock.bins</code> to control request bins from a test. You do not need an HTTP call:</p>
555
554
  <pre><code class="hljs language-javascript"><span class="hljs-keyword">import</span> { <span class="hljs-title class_">MockHttp</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'@jaredwray/mockhttp'</span>;
556
555
 
557
556
  <span class="hljs-keyword">const</span> mock = <span class="hljs-keyword">new</span> <span class="hljs-title class_">MockHttp</span>({ <span class="hljs-attr">logging</span>: <span class="hljs-literal">false</span> });
558
557
  <span class="hljs-keyword">await</span> mock.<span class="hljs-title function_">start</span>();
559
558
 
560
559
  <span class="hljs-comment">// Create a bin</span>
561
- <span class="hljs-keyword">const</span> bin = mock.<span class="hljs-property">bins</span>.<span class="hljs-title function_">createBin</span>();
560
+ <span class="hljs-keyword">const</span> bin = <span class="hljs-keyword">await</span> mock.<span class="hljs-property">bins</span>.<span class="hljs-title function_">createBin</span>();
562
561
  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`Webhook target: http://localhost:<span class="hljs-subst">${mock.port}</span>/b/<span class="hljs-subst">${bin.id}</span>`</span>);
563
562
 
564
563
  <span class="hljs-comment">// ... point your code-under-test at that URL ...</span>
565
564
 
566
565
  <span class="hljs-comment">// Read captured requests (newest first)</span>
567
- <span class="hljs-keyword">const</span> requests = mock.<span class="hljs-property">bins</span>.<span class="hljs-title function_">getRequests</span>(bin.<span class="hljs-property">id</span>);
566
+ <span class="hljs-keyword">const</span> requests = <span class="hljs-keyword">await</span> mock.<span class="hljs-property">bins</span>.<span class="hljs-title function_">getRequests</span>(bin.<span class="hljs-property">id</span>);
568
567
  <span class="hljs-variable language_">console</span>.<span class="hljs-title function_">log</span>(<span class="hljs-string">`Captured <span class="hljs-subst">${requests.length}</span> requests`</span>);
569
568
 
570
569
  <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> req <span class="hljs-keyword">of</span> requests) {
@@ -572,8 +571,8 @@ makes it easy to drive bins from a test suite without going through HTTP:</p>
572
571
  }
573
572
 
574
573
  <span class="hljs-comment">// Clean up</span>
575
- mock.<span class="hljs-property">bins</span>.<span class="hljs-title function_">deleteBin</span>(bin.<span class="hljs-property">id</span>);
576
- <span class="hljs-keyword">await</span> mock.<span class="hljs-title function_">close</span>(); <span class="hljs-comment">// also stops the bin cleanup timer</span>
574
+ <span class="hljs-keyword">await</span> mock.<span class="hljs-property">bins</span>.<span class="hljs-title function_">deleteBin</span>(bin.<span class="hljs-property">id</span>);
575
+ <span class="hljs-keyword">await</span> mock.<span class="hljs-title function_">close</span>();
577
576
  </code></pre>
578
577
  <h3 id="using-bins-in-a-vitest-test">Using Bins in a Vitest Test</h3>
579
578
  <pre><code class="hljs language-typescript"><span class="hljs-keyword">import</span> { afterAll, beforeAll, expect, test } <span class="hljs-keyword">from</span> <span class="hljs-string">'vitest'</span>;
@@ -591,14 +590,14 @@ mock.<span class="hljs-property">bins</span>.<span class="hljs-title function_">
591
590
  });
592
591
 
593
592
  <span class="hljs-title function_">test</span>(<span class="hljs-string">'my SDK sends the right webhook payload'</span>, <span class="hljs-title function_">async</span> () => {
594
- <span class="hljs-keyword">const</span> bin = mock.<span class="hljs-property">bins</span>.<span class="hljs-title function_">createBin</span>();
593
+ <span class="hljs-keyword">const</span> bin = <span class="hljs-keyword">await</span> mock.<span class="hljs-property">bins</span>.<span class="hljs-title function_">createBin</span>();
595
594
  <span class="hljs-keyword">const</span> webhookUrl = <span class="hljs-string">`http://localhost:<span class="hljs-subst">${mock.port}</span>/b/<span class="hljs-subst">${bin.id}</span>`</span>;
596
595
 
597
596
  <span class="hljs-comment">// Drive your SDK at the bin</span>
598
597
  <span class="hljs-keyword">await</span> mySdk.<span class="hljs-title function_">notify</span>(webhookUrl, { <span class="hljs-attr">event</span>: <span class="hljs-string">'user.created'</span>, <span class="hljs-attr">id</span>: <span class="hljs-number">42</span> });
599
598
 
600
599
  <span class="hljs-comment">// Assert on what actually arrived</span>
601
- <span class="hljs-keyword">const</span> [captured] = mock.<span class="hljs-property">bins</span>.<span class="hljs-title function_">getRequests</span>(bin.<span class="hljs-property">id</span>);
600
+ <span class="hljs-keyword">const</span> [captured] = <span class="hljs-keyword">await</span> mock.<span class="hljs-property">bins</span>.<span class="hljs-title function_">getRequests</span>(bin.<span class="hljs-property">id</span>);
602
601
  <span class="hljs-title function_">expect</span>(captured.<span class="hljs-property">method</span>).<span class="hljs-title function_">toBe</span>(<span class="hljs-string">'POST'</span>);
603
602
  <span class="hljs-title function_">expect</span>(captured.<span class="hljs-property">contentType</span>).<span class="hljs-title function_">toBe</span>(<span class="hljs-string">'application/json'</span>);
604
603
  <span class="hljs-title function_">expect</span>(<span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">parse</span>(captured.<span class="hljs-property">body</span>!)).<span class="hljs-title function_">toEqual</span>({
@@ -614,16 +613,15 @@ mock.<span class="hljs-property">bins</span>.<span class="hljs-title function_">
614
613
  <span class="hljs-attr">httpBin</span>: { <span class="hljs-attr">bins</span>: <span class="hljs-literal">false</span> },
615
614
  });
616
615
  </code></pre>
617
- <p>To tune limits, replace the default <code>BinManager</code> before starting:</p>
618
- <pre><code class="hljs language-javascript"><span class="hljs-keyword">import</span> { <span class="hljs-title class_">MockHttp</span>, <span class="hljs-title class_">BinManager</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'@jaredwray/mockhttp'</span>;
616
+ <p>To change the limits, replace the default <code>MockHttpBins</code> before you start the server:</p>
617
+ <pre><code class="hljs language-javascript"><span class="hljs-keyword">import</span> { <span class="hljs-title class_">MockHttp</span>, <span class="hljs-title class_">MockHttpBins</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'@jaredwray/mockhttp'</span>;
619
618
 
620
619
  <span class="hljs-keyword">const</span> mock = <span class="hljs-keyword">new</span> <span class="hljs-title class_">MockHttp</span>();
621
- mock.<span class="hljs-property">bins</span> = <span class="hljs-keyword">new</span> <span class="hljs-title class_">BinManager</span>({
620
+ mock.<span class="hljs-property">bins</span> = <span class="hljs-keyword">new</span> <span class="hljs-title class_">MockHttpBins</span>({
622
621
  <span class="hljs-attr">defaultTtlMs</span>: <span class="hljs-number">60</span> * <span class="hljs-number">60</span> * <span class="hljs-number">1000</span>, <span class="hljs-comment">// 1 hour (default: 24h)</span>
623
622
  <span class="hljs-attr">maxRequestsPerBin</span>: <span class="hljs-number">500</span>, <span class="hljs-comment">// (default: 100)</span>
624
- <span class="hljs-attr">maxBodySize</span>: <span class="hljs-number">5</span> * <span class="hljs-number">1024</span> * <span class="hljs-number">1024</span>, <span class="hljs-comment">// 5 MiB (default: 1 MiB)</span>
623
+ <span class="hljs-attr">maxBodySize</span>: <span class="hljs-number">512</span> * <span class="hljs-number">1024</span>, <span class="hljs-comment">// 512 KiB (default: 128 KiB)</span>
625
624
  <span class="hljs-attr">idLength</span>: <span class="hljs-number">16</span>, <span class="hljs-comment">// (default: 12)</span>
626
- <span class="hljs-attr">cleanupIntervalMs</span>: <span class="hljs-number">30</span> * <span class="hljs-number">1000</span>, <span class="hljs-comment">// sweep every 30s (default: 60s)</span>
627
625
  });
628
626
 
629
627
  <span class="hljs-keyword">await</span> mock.<span class="hljs-title function_">start</span>();
@@ -641,7 +639,7 @@ mock.<span class="hljs-property">bins</span> = <span class="hljs-keyword">new</s
641
639
  <tr>
642
640
  <td><code>defaultTtlMs</code></td>
643
641
  <td><code>86400000</code> (24h)</td>
644
- <td>Bin lifetime. Expired bins return 404 and are lazily removed.</td>
642
+ <td>Bin lifetime, set as the Keyv TTL. An expired bin returns 404.</td>
645
643
  </tr>
646
644
  <tr>
647
645
  <td><code>maxRequestsPerBin</code></td>
@@ -650,7 +648,7 @@ mock.<span class="hljs-property">bins</span> = <span class="hljs-keyword">new</s
650
648
  </tr>
651
649
  <tr>
652
650
  <td><code>maxBodySize</code></td>
653
- <td><code>1048576</code> (1 MiB)</td>
651
+ <td><code>131072</code> (128 KiB)</td>
654
652
  <td>Larger bodies are truncated; <code>truncated: true</code> is set on the capture.</td>
655
653
  </tr>
656
654
  <tr>
@@ -659,40 +657,29 @@ mock.<span class="hljs-property">bins</span> = <span class="hljs-keyword">new</s
659
657
  <td>Length of generated bin and request ids.</td>
660
658
  </tr>
661
659
  <tr>
662
- <td><code>cleanupIntervalMs</code></td>
663
- <td><code>60000</code> (1 min)</td>
664
- <td>How often expired bins are swept. The timer is <code>unref()</code>'d so it never keeps the process alive.</td>
660
+ <td><code>keyv</code></td>
661
+ <td><code>new Keyv()</code></td>
662
+ <td>Keyv store for bins. The default keeps bins in memory.</td>
665
663
  </tr>
666
664
  </tbody>
667
665
  </table>
668
- <h3 id="pluggable-storage">Pluggable Storage</h3>
669
- <p><code>BinManager</code> accepts a <code>store: BinStore</code> so you can plug in alternative
670
- backends (Redis, SQLite, etc.) without changing the rest of the codebase. The
671
- default <code>InMemoryBinStore</code> keeps state in process memory.</p>
672
- <pre><code class="hljs language-typescript"><span class="hljs-keyword">import</span> {
673
- <span class="hljs-title class_">BinManager</span>,
674
- <span class="hljs-title class_">InMemoryBinStore</span>,
675
- <span class="hljs-keyword">type</span> <span class="hljs-title class_">Bin</span>,
676
- <span class="hljs-keyword">type</span> <span class="hljs-title class_">BinStore</span>,
677
- <span class="hljs-keyword">type</span> <span class="hljs-title class_">CapturedRequest</span>,
678
- } <span class="hljs-keyword">from</span> <span class="hljs-string">'@jaredwray/mockhttp'</span>;
679
-
680
- <span class="hljs-keyword">class</span> <span class="hljs-title class_">RedisBinStore</span> <span class="hljs-keyword">implements</span> <span class="hljs-title class_">BinStore</span> {
681
- <span class="hljs-title function_">createBin</span>(<span class="hljs-attr">bin</span>: <span class="hljs-title class_">Bin</span>): <span class="hljs-built_in">void</span> { <span class="hljs-comment">/* SET bin:${bin.id} ... */</span> }
682
- <span class="hljs-title function_">getBin</span>(<span class="hljs-attr">id</span>: <span class="hljs-built_in">string</span>): <span class="hljs-title class_">Bin</span> | <span class="hljs-literal">undefined</span> { <span class="hljs-comment">/* GET ... */</span> }
683
- <span class="hljs-title function_">listBins</span>(): <span class="hljs-title class_">Bin</span>[] { <span class="hljs-comment">/* SCAN ... */</span> }
684
- <span class="hljs-title function_">deleteBin</span>(<span class="hljs-attr">id</span>: <span class="hljs-built_in">string</span>): <span class="hljs-built_in">boolean</span> { <span class="hljs-comment">/* DEL ... */</span> }
685
- <span class="hljs-title function_">addRequest</span>(<span class="hljs-attr">binId</span>: <span class="hljs-built_in">string</span>, <span class="hljs-attr">req</span>: <span class="hljs-title class_">CapturedRequest</span>, <span class="hljs-attr">max</span>: <span class="hljs-built_in">number</span>): <span class="hljs-built_in">void</span> {
686
- <span class="hljs-comment">/* LPUSH bin:${binId}:requests + LTRIM to max */</span>
687
- }
688
- <span class="hljs-title function_">getRequests</span>(<span class="hljs-attr">binId</span>: <span class="hljs-built_in">string</span>): <span class="hljs-title class_">CapturedRequest</span>[] { <span class="hljs-comment">/* LRANGE ... */</span> }
689
- <span class="hljs-title function_">getRequest</span>(<span class="hljs-attr">binId</span>: <span class="hljs-built_in">string</span>, <span class="hljs-attr">reqId</span>: <span class="hljs-built_in">string</span>): <span class="hljs-title class_">CapturedRequest</span> | <span class="hljs-literal">undefined</span> { <span class="hljs-comment">/* ... */</span> }
690
- <span class="hljs-title function_">clearRequests</span>(<span class="hljs-attr">binId</span>: <span class="hljs-built_in">string</span>): <span class="hljs-built_in">void</span> { <span class="hljs-comment">/* DEL bin:${binId}:requests */</span> }
691
- <span class="hljs-title function_">cleanupExpired</span>(<span class="hljs-attr">now</span>: <span class="hljs-built_in">number</span>): <span class="hljs-built_in">string</span>[] { <span class="hljs-comment">/* scan + delete */</span> <span class="hljs-keyword">return</span> []; }
692
- }
693
-
694
- mock.<span class="hljs-property">bins</span> = <span class="hljs-keyword">new</span> <span class="hljs-title class_">BinManager</span>({ <span class="hljs-attr">store</span>: <span class="hljs-keyword">new</span> <span class="hljs-title class_">RedisBinStore</span>() });
666
+ <h3 id="storage">Storage</h3>
667
+ <p><code>MockHttpBins</code> stores each bin in <a href="https://github.com/jaredwray/keyv">Keyv</a> 6. The default is <code>new Keyv()</code>. That store keeps the data in memory for this process.</p>
668
+ <p>Pass a Keyv 6 instance when you want a different adapter. Install the adapter in your project. This package does not include an adapter. <code>@keyv/redis</code> 6 matches this Keyv version.</p>
669
+ <pre><code class="hljs language-typescript"><span class="hljs-keyword">import</span> <span class="hljs-title class_">Keyv</span> <span class="hljs-keyword">from</span> <span class="hljs-string">'keyv'</span>;
670
+ <span class="hljs-keyword">import</span> <span class="hljs-title class_">KeyvRedis</span> <span class="hljs-keyword">from</span> <span class="hljs-string">'@keyv/redis'</span>; <span class="hljs-comment">// pnpm add @keyv/redis</span>
671
+ <span class="hljs-keyword">import</span> { <span class="hljs-title class_">MockHttpBins</span> } <span class="hljs-keyword">from</span> <span class="hljs-string">'@jaredwray/mockhttp'</span>;
672
+
673
+ mock.<span class="hljs-property">bins</span> = <span class="hljs-keyword">new</span> <span class="hljs-title class_">MockHttpBins</span>({
674
+ <span class="hljs-attr">keyv</span>: <span class="hljs-keyword">new</span> <span class="hljs-title class_">Keyv</span>({ <span class="hljs-attr">store</span>: <span class="hljs-keyword">new</span> <span class="hljs-title class_">KeyvRedis</span>(<span class="hljs-string">'redis://localhost:6379'</span>) }),
675
+ });
695
676
  </code></pre>
677
+ <p>Each capture writes the full bin again. A large body or a high request limit makes that write large. Set <code>maxBodySize</code> and <code>maxRequestsPerBin</code> to limit the size. With the defaults, a full bin of binary bodies is about 17 MB.</p>
678
+ <p>Two processes that share one Keyv can lose a capture. This occurs when both processes write the same bin at the same time.</p>
679
+ <p>If Keyv does not store a write, the bin route returns <code>500</code> with the standard Fastify error body: <code>statusCode</code>, <code>error</code>, and <code>message</code>.</p>
680
+ <p>A failed read depends on the Keyv <code>error</code> listener. Without a listener, Keyv throws the store error and the route returns <code>500</code>. With a listener, Keyv does not throw. A failed read then looks like a missing bin, and the route returns <code>404</code>. <code>listBins()</code> returns only the bins that it read before the error.</p>
681
+ <p><code>listBins()</code> throws an error when the adapter has no iterator.</p>
682
+ <p>An expired bin returns 404. The in-memory Keyv store deletes an expired bin only when a read or <code>listBins()</code> reaches it. Until then, the bin stays in memory.</p>
696
683
  </main>
697
684
  <aside class="content-aside">
698
685
  <h2 class="content-aside__title">TABLE OF CONTENTS</h2>
@@ -715,7 +702,7 @@ mock.<span class="hljs-property">bins</span> = <span class="hljs-keyword">new</s
715
702
  <a href="https://docula.org/" target="_blank" rel="noopener noreferrer" class="footer__link" aria-label="Docula">
716
703
  <svg class="footer__bat" xmlns="http://www.w3.org/2000/svg" viewBox="70 110 360 205" fill="currentColor"><path d="M420,173c-16.76-18-72.78-36.65-131.59-30.58A160.15,160.15,0,0,0,269,120.55c-3.78,4.52-11.15,9.45-19,9.45s-15.23-4.93-19-9.45a160.15,160.15,0,0,0-19.4,21.83C152.78,136.31,96.76,154.94,80,173c48.86,5.86,79.5,34.09,79.5,72.06,52.37,0,77,28,90.5,59.94C263.54,273,288.13,245,340.5,245,340.5,207,371.14,178.8,420,173Z"/><circle cx="239.19" cy="145.36" r="3.32" fill="#fff"/><circle cx="260.81" cy="145.36" r="3.32" fill="#fff"/></svg>
717
704
  </a>
718
- <span class="footer__updated">updated: 2026-10-03</span>
705
+ <span class="footer__updated">updated: 2026-10-20</span>
719
706
  </footer>
720
707
  <script src="/css/highlight/highlight.min.js"></script>
721
708
  <script>
@@ -35,7 +35,7 @@
35
35
  </script>
36
36
  <title>MockHTTP - Configuration</title>
37
37
  <script type="application/ld+json">
38
- {"@context":"https://schema.org","@type":"TechArticle","headline":"Configuration","description":"","url":"https://mockhttp.org/docs/configuration/","publisher":{"@type":"Organization","name":"MockHTTP"},"dateModified":"2026-10-03","keywords":[]}
38
+ {"@context":"https://schema.org","@type":"TechArticle","headline":"Configuration","description":"","url":"https://mockhttp.org/docs/configuration/","publisher":{"@type":"Organization","name":"MockHTTP"},"dateModified":"2026-10-20","keywords":[]}
39
39
  </script>
40
40
  </head>
41
41
 
@@ -544,7 +544,7 @@ GET /status/429/foo/bar
544
544
  <a href="https://docula.org/" target="_blank" rel="noopener noreferrer" class="footer__link" aria-label="Docula">
545
545
  <svg class="footer__bat" xmlns="http://www.w3.org/2000/svg" viewBox="70 110 360 205" fill="currentColor"><path d="M420,173c-16.76-18-72.78-36.65-131.59-30.58A160.15,160.15,0,0,0,269,120.55c-3.78,4.52-11.15,9.45-19,9.45s-15.23-4.93-19-9.45a160.15,160.15,0,0,0-19.4,21.83C152.78,136.31,96.76,154.94,80,173c48.86,5.86,79.5,34.09,79.5,72.06,52.37,0,77,28,90.5,59.94C263.54,273,288.13,245,340.5,245,340.5,207,371.14,178.8,420,173Z"/><circle cx="239.19" cy="145.36" r="3.32" fill="#fff"/><circle cx="260.81" cy="145.36" r="3.32" fill="#fff"/></svg>
546
546
  </a>
547
- <span class="footer__updated">updated: 2026-10-03</span>
547
+ <span class="footer__updated">updated: 2026-10-20</span>
548
548
  </footer>
549
549
  <script src="/css/highlight/highlight.min.js"></script>
550
550
  <script>
@@ -35,7 +35,7 @@
35
35
  </script>
36
36
  <title>MockHTTP - MockHttp.org Live</title>
37
37
  <script type="application/ld+json">
38
- {"@context":"https://schema.org","@type":"TechArticle","headline":"MockHttp.org Live","description":"","url":"https://mockhttp.org/docs/hosted/","publisher":{"@type":"Organization","name":"MockHTTP"},"dateModified":"2026-10-03","keywords":[]}
38
+ {"@context":"https://schema.org","@type":"TechArticle","headline":"MockHttp.org Live","description":"","url":"https://mockhttp.org/docs/hosted/","publisher":{"@type":"Organization","name":"MockHTTP"},"dateModified":"2026-10-20","keywords":[]}
39
39
  </script>
40
40
  </head>
41
41
 
@@ -293,7 +293,7 @@
293
293
  </header>
294
294
  <div class="article__body">
295
295
  <main class="article__main">
296
- <p><a href="https://mockhttp.org">mockhttp.org</a> is a free hosted instance of this codebase for testing. It runs on a <a href="/docs/how-to-deploy/cloudflare/">Cloudflare Worker</a> (no containers): documentation is served from Workers Assets, and mock APIs such as <code>/get</code> and <code>/post</code> run in the Worker. The service is globally available and rate-limited (1000 requests per minute per IP) to prevent abuse.</p>
296
+ <p><a href="https://mockhttp.org">mockhttp.org</a> is a free hosted instance of this codebase for testing. It runs on a <a href="/docs/how-to-deploy/cloudflare/">Cloudflare Worker</a> (no containers): documentation is served from Workers Assets, and mock APIs such as <code>/get</code> and <code>/post</code> run in the Worker. The service is globally available and rate-limited (1000 requests per minute per IP) to prevent abuse. Request bins on this service are stored in MongoDB Atlas, in the <code>bins</code> collection, so they are shared across isolates.</p>
297
297
  <p>All <a href="https://mockhttp.org">mockhttp.org</a> traffic stays on Cloudflare. A separate test app runs the Node server on <a href="/docs/how-to-deploy/wasmer/">Wasmer Edge</a> at <a href="https://mockhttp.wasmer.app">mockhttp.wasmer.app</a>.</p>
298
298
  <p>Getting Started is served at <code>/</code> and <code>/docs</code>, other guides at <code>/docs/...</code>, and the interactive HTTP API reference at <code>/api</code>. Mock endpoints such as <code>/get</code> and <code>/post</code> are unchanged.</p>
299
299
  </main>
@@ -318,7 +318,7 @@
318
318
  <a href="https://docula.org/" target="_blank" rel="noopener noreferrer" class="footer__link" aria-label="Docula">
319
319
  <svg class="footer__bat" xmlns="http://www.w3.org/2000/svg" viewBox="70 110 360 205" fill="currentColor"><path d="M420,173c-16.76-18-72.78-36.65-131.59-30.58A160.15,160.15,0,0,0,269,120.55c-3.78,4.52-11.15,9.45-19,9.45s-15.23-4.93-19-9.45a160.15,160.15,0,0,0-19.4,21.83C152.78,136.31,96.76,154.94,80,173c48.86,5.86,79.5,34.09,79.5,72.06,52.37,0,77,28,90.5,59.94C263.54,273,288.13,245,340.5,245,340.5,207,371.14,178.8,420,173Z"/><circle cx="239.19" cy="145.36" r="3.32" fill="#fff"/><circle cx="260.81" cy="145.36" r="3.32" fill="#fff"/></svg>
320
320
  </a>
321
- <span class="footer__updated">updated: 2026-10-03</span>
321
+ <span class="footer__updated">updated: 2026-10-20</span>
322
322
  </footer>
323
323
  <script src="/css/highlight/highlight.min.js"></script>
324
324
  <script>
@@ -35,7 +35,7 @@
35
35
  </script>
36
36
  <title>MockHTTP - Cloudflare</title>
37
37
  <script type="application/ld+json">
38
- {"@context":"https://schema.org","@type":"TechArticle","headline":"Cloudflare","description":"Deploy MockHTTP as a Cloudflare Worker. Workers Assets serve the docs site, and the Worker handles mock API routes.","url":"https://mockhttp.org/docs/how-to-deploy/cloudflare/","publisher":{"@type":"Organization","name":"MockHTTP"},"dateModified":"2026-10-03","keywords":[]}
38
+ {"@context":"https://schema.org","@type":"TechArticle","headline":"Cloudflare","description":"Deploy MockHTTP as a Cloudflare Worker. Workers Assets serve the docs site, and the Worker handles mock API routes.","url":"https://mockhttp.org/docs/how-to-deploy/cloudflare/","publisher":{"@type":"Organization","name":"MockHTTP"},"dateModified":"2026-10-20","keywords":[]}
39
39
  </script>
40
40
  </head>
41
41
 
@@ -329,11 +329,6 @@
329
329
  <td>Logos and other <code>public/</code> files are copied into <code>site/dist</code> and read through the <code>ASSETS</code> binding.</td>
330
330
  </tr>
331
331
  <tr>
332
- <td><code>startBins</code></td>
333
- <td><code>false</code></td>
334
- <td>The bin cleanup timer uses <code>setInterval</code>, which can hang a Worker isolate during startup. Bin routes still register. Expired bins are removed when that bin is read, not by a sweep of every bin.</td>
335
- </tr>
336
- <tr>
337
332
  <td><code>pluginTimeout</code></td>
338
333
  <td><code>0</code></td>
339
334
  <td>Disables Fastify's plugin boot timeout. Those timers are unreliable on Workers.</td>
@@ -390,7 +385,7 @@ npx wrangler@4.127.0 deploy
390
385
  <a href="https://docula.org/" target="_blank" rel="noopener noreferrer" class="footer__link" aria-label="Docula">
391
386
  <svg class="footer__bat" xmlns="http://www.w3.org/2000/svg" viewBox="70 110 360 205" fill="currentColor"><path d="M420,173c-16.76-18-72.78-36.65-131.59-30.58A160.15,160.15,0,0,0,269,120.55c-3.78,4.52-11.15,9.45-19,9.45s-15.23-4.93-19-9.45a160.15,160.15,0,0,0-19.4,21.83C152.78,136.31,96.76,154.94,80,173c48.86,5.86,79.5,34.09,79.5,72.06,52.37,0,77,28,90.5,59.94C263.54,273,288.13,245,340.5,245,340.5,207,371.14,178.8,420,173Z"/><circle cx="239.19" cy="145.36" r="3.32" fill="#fff"/><circle cx="260.81" cy="145.36" r="3.32" fill="#fff"/></svg>
392
387
  </a>
393
- <span class="footer__updated">updated: 2026-10-03</span>
388
+ <span class="footer__updated">updated: 2026-10-20</span>
394
389
  </footer>
395
390
  <script src="/css/highlight/highlight.min.js"></script>
396
391
  <script>
@@ -35,7 +35,7 @@
35
35
  </script>
36
36
  <title>MockHTTP - Docker</title>
37
37
  <script type="application/ld+json">
38
- {"@context":"https://schema.org","@type":"TechArticle","headline":"Docker","description":"Run MockHTTP from the jaredwray/mockhttp image, with Docker Compose, or by building the image from this repository.","url":"https://mockhttp.org/docs/how-to-deploy/docker/","publisher":{"@type":"Organization","name":"MockHTTP"},"dateModified":"2026-10-03","keywords":[]}
38
+ {"@context":"https://schema.org","@type":"TechArticle","headline":"Docker","description":"Run MockHTTP from the jaredwray/mockhttp image, with Docker Compose, or by building the image from this repository.","url":"https://mockhttp.org/docs/how-to-deploy/docker/","publisher":{"@type":"Organization","name":"MockHTTP"},"dateModified":"2026-10-20","keywords":[]}
39
39
  </script>
40
40
  </head>
41
41
 
@@ -399,7 +399,7 @@ docker run -d -p 3000:3000 jaredwray/mockhttp
399
399
  <a href="https://docula.org/" target="_blank" rel="noopener noreferrer" class="footer__link" aria-label="Docula">
400
400
  <svg class="footer__bat" xmlns="http://www.w3.org/2000/svg" viewBox="70 110 360 205" fill="currentColor"><path d="M420,173c-16.76-18-72.78-36.65-131.59-30.58A160.15,160.15,0,0,0,269,120.55c-3.78,4.52-11.15,9.45-19,9.45s-15.23-4.93-19-9.45a160.15,160.15,0,0,0-19.4,21.83C152.78,136.31,96.76,154.94,80,173c48.86,5.86,79.5,34.09,79.5,72.06,52.37,0,77,28,90.5,59.94C263.54,273,288.13,245,340.5,245,340.5,207,371.14,178.8,420,173Z"/><circle cx="239.19" cy="145.36" r="3.32" fill="#fff"/><circle cx="260.81" cy="145.36" r="3.32" fill="#fff"/></svg>
401
401
  </a>
402
- <span class="footer__updated">updated: 2026-10-03</span>
402
+ <span class="footer__updated">updated: 2026-10-20</span>
403
403
  </footer>
404
404
  <script src="/css/highlight/highlight.min.js"></script>
405
405
  <script>
@@ -35,7 +35,7 @@
35
35
  </script>
36
36
  <title>MockHTTP - Wasmer</title>
37
37
  <script type="application/ld+json">
38
- {"@context":"https://schema.org","@type":"TechArticle","headline":"Wasmer","description":"Deploy the MockHTTP Node.js server to Wasmer Edge with app.yaml, Anybuild, and wasmer deploy.","url":"https://mockhttp.org/docs/how-to-deploy/wasmer/","publisher":{"@type":"Organization","name":"MockHTTP"},"dateModified":"2026-10-03","keywords":[]}
38
+ {"@context":"https://schema.org","@type":"TechArticle","headline":"Wasmer","description":"Deploy the MockHTTP Node.js server to Wasmer Edge with app.yaml, Anybuild, and wasmer deploy.","url":"https://mockhttp.org/docs/how-to-deploy/wasmer/","publisher":{"@type":"Organization","name":"MockHTTP"},"dateModified":"2026-10-20","keywords":[]}
39
39
  </script>
40
40
  </head>
41
41
 
@@ -320,7 +320,7 @@
320
320
  <h2 id="build-with-anybuild">Build with Anybuild</h2>
321
321
  <p><a href="https://github.com/jaredwray/mockhttp/blob/main/Anybuild"><code>Anybuild</code></a> tells Wasmer how to build the package on a remote builder:</p>
322
322
  <ul>
323
- <li>Node.js 24 and pnpm <code>12.4.1</code></li>
323
+ <li>Node.js 24 and pnpm <code>12.12.1</code></li>
324
324
  <li>Build command: <code>pnpm run build &#x26;&#x26; pnpm website:build</code></li>
325
325
  <li>Start command: <code>node dist/index.mjs</code></li>
326
326
  </ul>
@@ -335,7 +335,7 @@
335
335
  <pre><code class="hljs language-bash">wasmer deploy --non-interactive --build-remote
336
336
  </code></pre>
337
337
  <h2 id="runtime-behavior">Runtime behavior</h2>
338
- <p>Bins use the default in-memory store, so a bin exists only on the instance that created it. Taps are a library API (<code>mock.taps.inject</code>) and have no HTTP routes, so a deployed app cannot inject one.</p>
338
+ <p>Bins use <code>new Keyv()</code> by default. The store keeps each bin in memory on the instance that created it. Give <code>MockHttpBins</code> a shared Keyv when bins must be visible on more than one instance. Two processes can lose a capture when they write the same bin at the same time. Taps are a library API (<code>mock.taps.inject</code>) and have no HTTP routes, so a deployed app cannot inject one.</p>
339
339
  <p><code>@fastify/rate-limit</code> is on at 1000 requests per minute. It keys on the address that connects to the process. The server does not trust proxy headers, and the allow list is only <code>127.0.0.1</code> and <code>::1</code>. On the test app, <code>/ip</code> reports <code>127.0.0.100</code>, which is the edge proxy rather than the caller, and the response includes <code>x-ratelimit-limit: 1000</code>. Clients that share that proxy address share one limit on that instance. This is not the Cloudflare limiter used by <a href="https://mockhttp.org">mockhttp.org</a>.</p>
340
340
  <p>The process listens with HTTP. Wasmer terminates TLS for the <code>*.wasmer.app</code> hostname.</p>
341
341
  </main>
@@ -360,7 +360,7 @@
360
360
  <a href="https://docula.org/" target="_blank" rel="noopener noreferrer" class="footer__link" aria-label="Docula">
361
361
  <svg class="footer__bat" xmlns="http://www.w3.org/2000/svg" viewBox="70 110 360 205" fill="currentColor"><path d="M420,173c-16.76-18-72.78-36.65-131.59-30.58A160.15,160.15,0,0,0,269,120.55c-3.78,4.52-11.15,9.45-19,9.45s-15.23-4.93-19-9.45a160.15,160.15,0,0,0-19.4,21.83C152.78,136.31,96.76,154.94,80,173c48.86,5.86,79.5,34.09,79.5,72.06,52.37,0,77,28,90.5,59.94C263.54,273,288.13,245,340.5,245,340.5,207,371.14,178.8,420,173Z"/><circle cx="239.19" cy="145.36" r="3.32" fill="#fff"/><circle cx="260.81" cy="145.36" r="3.32" fill="#fff"/></svg>
362
362
  </a>
363
- <span class="footer__updated">updated: 2026-10-03</span>
363
+ <span class="footer__updated">updated: 2026-10-20</span>
364
364
  </footer>
365
365
  <script src="/css/highlight/highlight.min.js"></script>
366
366
  <script>
@@ -35,7 +35,7 @@
35
35
  </script>
36
36
  <title>MockHTTP - HTTPS and HTTP/2</title>
37
37
  <script type="application/ld+json">
38
- {"@context":"https://schema.org","@type":"TechArticle","headline":"HTTPS and HTTP/2","description":"","url":"https://mockhttp.org/docs/https/","publisher":{"@type":"Organization","name":"MockHTTP"},"dateModified":"2026-10-03","keywords":[]}
38
+ {"@context":"https://schema.org","@type":"TechArticle","headline":"HTTPS and HTTP/2","description":"","url":"https://mockhttp.org/docs/https/","publisher":{"@type":"Organization","name":"MockHTTP"},"dateModified":"2026-10-20","keywords":[]}
39
39
  </script>
40
40
  </head>
41
41
 
@@ -531,7 +531,7 @@
531
531
  <a href="https://docula.org/" target="_blank" rel="noopener noreferrer" class="footer__link" aria-label="Docula">
532
532
  <svg class="footer__bat" xmlns="http://www.w3.org/2000/svg" viewBox="70 110 360 205" fill="currentColor"><path d="M420,173c-16.76-18-72.78-36.65-131.59-30.58A160.15,160.15,0,0,0,269,120.55c-3.78,4.52-11.15,9.45-19,9.45s-15.23-4.93-19-9.45a160.15,160.15,0,0,0-19.4,21.83C152.78,136.31,96.76,154.94,80,173c48.86,5.86,79.5,34.09,79.5,72.06,52.37,0,77,28,90.5,59.94C263.54,273,288.13,245,340.5,245,340.5,207,371.14,178.8,420,173Z"/><circle cx="239.19" cy="145.36" r="3.32" fill="#fff"/><circle cx="260.81" cy="145.36" r="3.32" fill="#fff"/></svg>
533
533
  </a>
534
- <span class="footer__updated">updated: 2026-10-03</span>
534
+ <span class="footer__updated">updated: 2026-10-20</span>
535
535
  </footer>
536
536
  <script src="/css/highlight/highlight.min.js"></script>
537
537
  <script>
@@ -35,7 +35,7 @@
35
35
  </script>
36
36
  <title>MockHTTP - Getting Started</title>
37
37
  <script type="application/ld+json">
38
- {"@context":"https://schema.org","@type":"TechArticle","headline":"Getting Started","description":"","url":"https://mockhttp.org/docs/","publisher":{"@type":"Organization","name":"MockHTTP"},"dateModified":"2026-10-03","keywords":[]}
38
+ {"@context":"https://schema.org","@type":"TechArticle","headline":"Getting Started","description":"","url":"https://mockhttp.org/docs/","publisher":{"@type":"Organization","name":"MockHTTP"},"dateModified":"2026-10-20","keywords":[]}
39
39
  </script>
40
40
  </head>
41
41
 
@@ -375,7 +375,7 @@
375
375
  <a href="https://docula.org/" target="_blank" rel="noopener noreferrer" class="footer__link" aria-label="Docula">
376
376
  <svg class="footer__bat" xmlns="http://www.w3.org/2000/svg" viewBox="70 110 360 205" fill="currentColor"><path d="M420,173c-16.76-18-72.78-36.65-131.59-30.58A160.15,160.15,0,0,0,269,120.55c-3.78,4.52-11.15,9.45-19,9.45s-15.23-4.93-19-9.45a160.15,160.15,0,0,0-19.4,21.83C152.78,136.31,96.76,154.94,80,173c48.86,5.86,79.5,34.09,79.5,72.06,52.37,0,77,28,90.5,59.94C263.54,273,288.13,245,340.5,245,340.5,207,371.14,178.8,420,173Z"/><circle cx="239.19" cy="145.36" r="3.32" fill="#fff"/><circle cx="260.81" cy="145.36" r="3.32" fill="#fff"/></svg>
377
377
  </a>
378
- <span class="footer__updated">updated: 2026-10-03</span>
378
+ <span class="footer__updated">updated: 2026-10-20</span>
379
379
  </footer>
380
380
  <script src="/css/highlight/highlight.min.js"></script>
381
381
  <script>
@@ -35,7 +35,7 @@
35
35
  </script>
36
36
  <title>MockHTTP - Library API</title>
37
37
  <script type="application/ld+json">
38
- {"@context":"https://schema.org","@type":"TechArticle","headline":"Library API","description":"","url":"https://mockhttp.org/docs/library/","publisher":{"@type":"Organization","name":"MockHTTP"},"dateModified":"2026-10-03","keywords":[]}
38
+ {"@context":"https://schema.org","@type":"TechArticle","headline":"Library API","description":"","url":"https://mockhttp.org/docs/library/","publisher":{"@type":"Organization","name":"MockHTTP"},"dateModified":"2026-10-20","keywords":[]}
39
39
  </script>
40
40
  </head>
41
41
 
@@ -333,7 +333,6 @@
333
333
  <li><a href="#binsgetrequestbinid-reqid"><code>bins.getRequest(binId, reqId)</code></a></li>
334
334
  <li><a href="#binsclearrequestsbinid"><code>bins.clearRequests(binId)</code></a></li>
335
335
  <li><a href="#binsrecordrequestbinid-raw"><code>bins.recordRequest(binId, raw)</code></a></li>
336
- <li><a href="#binsstart--binsstop"><code>bins.start()</code> / <code>bins.stop()</code></a></li>
337
336
  <li><a href="#binsmaxbodysize--binsmaxrequestsperbin--binsdefaultttlms"><code>bins.maxBodySize</code> / <code>bins.maxRequestsPerBin</code> / <code>bins.defaultTtlMs</code></a></li>
338
337
  </ul>
339
338
  </li>
@@ -368,7 +367,6 @@
368
367
  <li><code>logging?</code>: boolean - Enable logging (default: true)</li>
369
368
  <li><code>staticFiles?</code>: boolean - Serve files from the package <code>public/</code> directory (default: true)</li>
370
369
  <li><code>pluginTimeout?</code>: number - Fastify plugin boot timeout in milliseconds; <code>0</code> disables it (default: Fastify's timeout)</li>
371
- <li><code>startBins?</code>: boolean - Start the request-bin cleanup timer during <code>initialize()</code> (default: true)</li>
372
370
  <li><code>httpBin?</code>: HttpBinOptions - Configure which httpbin routes to enable
373
371
  <ul>
374
372
  <li><code>httpMethods?</code>: boolean - Enable HTTP method routes (default: true)</li>
@@ -402,7 +400,6 @@
402
400
  <li><code>logging</code>: boolean - Get/set logging enabled state</li>
403
401
  <li><code>staticFiles</code>: boolean - Get/set whether files from <code>public/</code> are served</li>
404
402
  <li><code>pluginTimeout</code>: number | undefined - Get/set Fastify plugin boot timeout</li>
405
- <li><code>startBins</code>: boolean - Get/set whether bin cleanup starts during <code>initialize()</code></li>
406
403
  <li><code>rateLimit</code>: RateLimitPluginOptions | undefined - Get/set rate limiting options</li>
407
404
  <li><code>httpBin</code>: HttpBinOptions - Get/set httpbin route options</li>
408
405
  <li><code>https</code>: HttpsOptions | undefined - Get/set HTTPS configuration</li>
@@ -411,7 +408,7 @@
411
408
  <li><code>http1</code>: boolean - Get/set HTTP/1.1 fallback for HTTP/2 with HTTPS</li>
412
409
  <li><code>server</code>: FastifyInstance - Get/set the Fastify server instance</li>
413
410
  <li><code>taps</code>: TapManager - Get/set the TapManager instance</li>
414
- <li><code>bins</code>: BinManager - Get/set the BinManager instance for request bins</li>
411
+ <li><code>bins</code>: MockHttpBins - Get or set the request bins</li>
415
412
  </ul>
416
413
  <h3 id="methods">Methods</h3>
417
414
  <h4 id="async-initialize"><code>async initialize()</code></h4>
@@ -452,32 +449,31 @@
452
449
  <h4 id="async-registerbinroutesfastifyinstance"><code>async registerBinRoutes(fastifyInstance?)</code></h4>
453
450
  <p>Register the request bin routes — management at <code>/bins</code> and capture at <code>/b/:id</code>.</p>
454
451
  <h2 id="bins-request-capture">Bins (Request Capture)</h2>
455
- <p>Access the BinManager via <code>mockHttp.bins</code> to manage request bins programmatically. See <a href="#request-bins">Request Bins</a> for usage examples.</p>
452
+ <p>Use <code>mockHttp.bins</code> to control request bins. See <a href="#request-bins">Request Bins</a> for usage examples.</p>
456
453
  <h3 id="binscreatebin"><code>bins.createBin()</code></h3>
457
- <p>Create a new bin with the configured TTL.</p>
458
- <p><strong>Returns:</strong> <code>Bin</code> — <code>{ id, createdAt, expiresAt, requestCount }</code></p>
454
+ <p>Create a new bin with the configured TTL. Throws when Keyv does not store the bin.</p>
455
+ <p><strong>Returns:</strong> <code>Promise&#x3C;Bin></code> — <code>{ id, createdAt, expiresAt, requestCount }</code></p>
459
456
  <h3 id="binsgetbinid"><code>bins.getBin(id)</code></h3>
460
- <p>Look up a bin by id. Returns <code>undefined</code> if the bin does not exist or has expired (and lazily removes the expired entry).</p>
461
- <p><strong>Returns:</strong> <code>Bin | undefined</code></p>
457
+ <p>Look up a bin by id. Returns <code>undefined</code> if the bin does not exist or Keyv has expired it. When the Keyv instance has an <code>error</code> listener, a failed read also returns <code>undefined</code>.</p>
458
+ <p><strong>Returns:</strong> <code>Promise&#x3C;Bin | undefined></code></p>
462
459
  <h3 id="binslistbins"><code>bins.listBins()</code></h3>
463
- <p>List all non-expired bins.</p>
464
- <p><strong>Returns:</strong> <code>Bin[]</code></p>
460
+ <p>List the bins that Keyv still holds.</p>
461
+ <p><strong>Returns:</strong> <code>Promise&#x3C;Bin[]></code></p>
465
462
  <h3 id="binsdeletebinid"><code>bins.deleteBin(id)</code></h3>
466
- <p>Delete a bin and all its captured requests.</p>
467
- <p><strong>Returns:</strong> <code>boolean</code> — <code>true</code> if the bin existed</p>
463
+ <p>Delete a bin and all its captured requests. The delete waits for captures already queued for that bin.</p>
464
+ <p><strong>Returns:</strong> <code>Promise&#x3C;boolean></code> — <code>true</code> when the bin existed</p>
468
465
  <h3 id="binsgetrequestsbinid"><code>bins.getRequests(binId)</code></h3>
469
466
  <p>List captured requests for a bin, newest first.</p>
470
- <p><strong>Returns:</strong> <code>CapturedRequest[]</code></p>
467
+ <p><strong>Returns:</strong> <code>Promise&#x3C;CapturedRequest[]></code></p>
471
468
  <h3 id="binsgetrequestbinid-reqid"><code>bins.getRequest(binId, reqId)</code></h3>
472
469
  <p>Look up a single captured request.</p>
473
- <p><strong>Returns:</strong> <code>CapturedRequest | undefined</code></p>
470
+ <p><strong>Returns:</strong> <code>Promise&#x3C;CapturedRequest | undefined></code></p>
474
471
  <h3 id="binsclearrequestsbinid"><code>bins.clearRequests(binId)</code></h3>
475
- <p>Clear all captured requests in a bin (the bin itself is preserved).</p>
472
+ <p>Clear all captured requests in a bin. The bin stays. Throws when Keyv does not store the change.</p>
473
+ <p><strong>Returns:</strong> <code>Promise&#x3C;boolean></code> — <code>false</code> when the bin does not exist</p>
476
474
  <h3 id="binsrecordrequestbinid-raw"><code>bins.recordRequest(binId, raw)</code></h3>
477
- <p>Manually record a captured request. Returns <code>undefined</code> if the bin does not exist or is expired. Normally invoked by the capture route, but available for programmatic use.</p>
478
- <p><strong>Returns:</strong> <code>CapturedRequest | undefined</code></p>
479
- <h3 id="binsstart--binsstop"><code>bins.start()</code> / <code>bins.stop()</code></h3>
480
- <p>Start or stop the periodic cleanup of expired bins. <code>MockHttp.start()</code> and <code>MockHttp.close()</code> call these automatically. The interval is <code>unref()</code>'d so it never keeps the Node process alive.</p>
475
+ <p>Manually record a captured request. Returns <code>undefined</code> if the bin does not exist. Throws when Keyv does not store the capture. Normally invoked by the capture route, but available for programmatic use.</p>
476
+ <p><strong>Returns:</strong> <code>Promise&#x3C;CapturedRequest | undefined></code></p>
481
477
  <h3 id="binsmaxbodysize--binsmaxrequestsperbin--binsdefaultttlms"><code>bins.maxBodySize</code> / <code>bins.maxRequestsPerBin</code> / <code>bins.defaultTtlMs</code></h3>
482
478
  <p>Read-only getters for the currently-configured limits.</p>
483
479
  <h2 id="taps-response-injection">Taps (Response Injection)</h2>
@@ -553,7 +549,7 @@
553
549
  <a href="https://docula.org/" target="_blank" rel="noopener noreferrer" class="footer__link" aria-label="Docula">
554
550
  <svg class="footer__bat" xmlns="http://www.w3.org/2000/svg" viewBox="70 110 360 205" fill="currentColor"><path d="M420,173c-16.76-18-72.78-36.65-131.59-30.58A160.15,160.15,0,0,0,269,120.55c-3.78,4.52-11.15,9.45-19,9.45s-15.23-4.93-19-9.45a160.15,160.15,0,0,0-19.4,21.83C152.78,136.31,96.76,154.94,80,173c48.86,5.86,79.5,34.09,79.5,72.06,52.37,0,77,28,90.5,59.94C263.54,273,288.13,245,340.5,245,340.5,207,371.14,178.8,420,173Z"/><circle cx="239.19" cy="145.36" r="3.32" fill="#fff"/><circle cx="260.81" cy="145.36" r="3.32" fill="#fff"/></svg>
555
551
  </a>
556
- <span class="footer__updated">updated: 2026-10-03</span>
552
+ <span class="footer__updated">updated: 2026-10-20</span>
557
553
  </footer>
558
554
  <script src="/css/highlight/highlight.min.js"></script>
559
555
  <script>
@@ -35,7 +35,7 @@
35
35
  </script>
36
36
  <title>MockHTTP - Taps</title>
37
37
  <script type="application/ld+json">
38
- {"@context":"https://schema.org","@type":"TechArticle","headline":"Taps","description":"","url":"https://mockhttp.org/docs/taps/","publisher":{"@type":"Organization","name":"MockHTTP"},"dateModified":"2026-10-03","keywords":[]}
38
+ {"@context":"https://schema.org","@type":"TechArticle","headline":"Taps","description":"","url":"https://mockhttp.org/docs/taps/","publisher":{"@type":"Organization","name":"MockHTTP"},"dateModified":"2026-10-20","keywords":[]}
39
39
  </script>
40
40
  </head>
41
41
 
@@ -481,7 +481,7 @@ mock.<span class="hljs-property">taps</span>.<span class="hljs-title function_">
481
481
  <a href="https://docula.org/" target="_blank" rel="noopener noreferrer" class="footer__link" aria-label="Docula">
482
482
  <svg class="footer__bat" xmlns="http://www.w3.org/2000/svg" viewBox="70 110 360 205" fill="currentColor"><path d="M420,173c-16.76-18-72.78-36.65-131.59-30.58A160.15,160.15,0,0,0,269,120.55c-3.78,4.52-11.15,9.45-19,9.45s-15.23-4.93-19-9.45a160.15,160.15,0,0,0-19.4,21.83C152.78,136.31,96.76,154.94,80,173c48.86,5.86,79.5,34.09,79.5,72.06,52.37,0,77,28,90.5,59.94C263.54,273,288.13,245,340.5,245,340.5,207,371.14,178.8,420,173Z"/><circle cx="239.19" cy="145.36" r="3.32" fill="#fff"/><circle cx="260.81" cy="145.36" r="3.32" fill="#fff"/></svg>
483
483
  </a>
484
- <span class="footer__updated">updated: 2026-10-03</span>
484
+ <span class="footer__updated">updated: 2026-10-20</span>
485
485
  </footer>
486
486
  <script src="/css/highlight/highlight.min.js"></script>
487
487
  <script>
@@ -1 +1 @@
1
- <?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>MockHTTP</title><link>https://mockhttp.org/</link><description>HTTP mock server and httpbin replacement for API testing. Run it at mockhttp.org, in Docker, or in Node.js.</description><lastBuildDate>Sat, 03 Oct 2026 22:17:09 GMT</lastBuildDate><atom:link href="https://mockhttp.org/feed.xml" rel="self" type="application/rss+xml" /><item><title>Getting Started</title><link>https://mockhttp.org/docs/</link><guid isPermaLink="true">https://mockhttp.org/docs/</guid><description>tests GitHub license codecov npm npm Docker Pulls mockhttp.org A simple HTTP server for mocking responses in tests. Inspired by httpbin and built with Node.js and Fastify. Run it at mockhttp.org, with Docker (jaredwray/mockhttp), or in Node...</description></item><item><title>HTTPS and HTTP/2</title><link>https://mockhttp.org/docs/https/</link><guid isPermaLink="true">https://mockhttp.org/docs/https/</guid><description>MockHttp supports HTTPS with auto-generated self-signed certificates or your own custom certificates. No external dependencies are required — certificate generation uses only Node.js built-in crypto. The simplest way to enable HTTPS is to p...</description></item><item><title>Taps</title><link>https://mockhttp.org/docs/taps/</link><guid isPermaLink="true">https://mockhttp.org/docs/taps/</guid><description>The injection/tap feature allows you to &quot;tap into&quot; the request flow and inject custom responses for specific requests. This is particularly useful for: Offline testing - Mock external API responses without network access Testing edge cases...</description></item><item><title>Bins</title><link>https://mockhttp.org/docs/bins/</link><guid isPermaLink="true">https://mockhttp.org/docs/bins/</guid><description>Bins are ephemeral URL endpoints that capture incoming HTTP requests so you can inspect them later. They&apos;re the inverse of Taps: instead of injecting a response, they record everything they receive. Useful for: Debugging webhooks — point St...</description></item><item><title>Configuration</title><link>https://mockhttp.org/docs/configuration/</link><guid isPermaLink="true">https://mockhttp.org/docs/configuration/</guid><description>MockHttp supports rate limiting using @fastify/rate-limit. Rate limiting is enabled by default at 1000 requests per minute with localhost (127.0.0.1 and ::1) excluded from rate limiting. By default, MockHttp applies the following rate limit...</description></item><item><title>Library API</title><link>https://mockhttp.org/docs/library/</link><guid isPermaLink="true">https://mockhttp.org/docs/library/</guid><description>Parameters: options? (MockHttpOptions): port?: number - The port to listen on (default: 3000) host?: string - The host to listen on (default: &apos;0.0.0.0&apos;) autoDetectPort?: boolean - Auto-detect next available port if in use (default: true) he...</description></item><item><title>MockHttp.org Live</title><link>https://mockhttp.org/docs/hosted/</link><guid isPermaLink="true">https://mockhttp.org/docs/hosted/</guid><description>mockhttp.org is a free hosted instance of this codebase for testing. It runs on a Cloudflare Worker (no containers): documentation is served from Workers Assets, and mock APIs such as /get and /post run in the Worker. The service is globall...</description></item><item><title>Cloudflare</title><link>https://mockhttp.org/docs/how-to-deploy/cloudflare/</link><guid isPermaLink="true">https://mockhttp.org/docs/how-to-deploy/cloudflare/</guid><description>Deploy MockHTTP as a Cloudflare Worker. Workers Assets serve the docs site, and the Worker handles mock API routes.</description></item><item><title>Docker</title><link>https://mockhttp.org/docs/how-to-deploy/docker/</link><guid isPermaLink="true">https://mockhttp.org/docs/how-to-deploy/docker/</guid><description>Run MockHTTP from the jaredwray/mockhttp image, with Docker Compose, or by building the image from this repository.</description></item><item><title>Wasmer</title><link>https://mockhttp.org/docs/how-to-deploy/wasmer/</link><guid isPermaLink="true">https://mockhttp.org/docs/how-to-deploy/wasmer/</guid><description>Deploy the MockHTTP Node.js server to Wasmer Edge with app.yaml, Anybuild, and wasmer deploy.</description></item></channel></rss>
1
+ <?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>MockHTTP</title><link>https://mockhttp.org/</link><description>HTTP mock server and httpbin replacement for API testing. Run it at mockhttp.org, in Docker, or in Node.js.</description><lastBuildDate>Tue, 20 Oct 2026 00:19:31 GMT</lastBuildDate><atom:link href="https://mockhttp.org/feed.xml" rel="self" type="application/rss+xml" /><item><title>Getting Started</title><link>https://mockhttp.org/docs/</link><guid isPermaLink="true">https://mockhttp.org/docs/</guid><description>tests GitHub license codecov npm npm Docker Pulls mockhttp.org A simple HTTP server for mocking responses in tests. Inspired by httpbin and built with Node.js and Fastify. Run it at mockhttp.org, with Docker (jaredwray/mockhttp), or in Node...</description></item><item><title>HTTPS and HTTP/2</title><link>https://mockhttp.org/docs/https/</link><guid isPermaLink="true">https://mockhttp.org/docs/https/</guid><description>MockHttp supports HTTPS with auto-generated self-signed certificates or your own custom certificates. No external dependencies are required — certificate generation uses only Node.js built-in crypto. The simplest way to enable HTTPS is to p...</description></item><item><title>Taps</title><link>https://mockhttp.org/docs/taps/</link><guid isPermaLink="true">https://mockhttp.org/docs/taps/</guid><description>The injection/tap feature allows you to &quot;tap into&quot; the request flow and inject custom responses for specific requests. This is particularly useful for: Offline testing - Mock external API responses without network access Testing edge cases...</description></item><item><title>Bins</title><link>https://mockhttp.org/docs/bins/</link><guid isPermaLink="true">https://mockhttp.org/docs/bins/</guid><description>Bins are ephemeral URL endpoints that capture incoming HTTP requests so you can inspect them later. They&apos;re the inverse of Taps: instead of injecting a response, they record everything they receive. Useful for: Debugging webhooks — point St...</description></item><item><title>Configuration</title><link>https://mockhttp.org/docs/configuration/</link><guid isPermaLink="true">https://mockhttp.org/docs/configuration/</guid><description>MockHttp supports rate limiting using @fastify/rate-limit. Rate limiting is enabled by default at 1000 requests per minute with localhost (127.0.0.1 and ::1) excluded from rate limiting. By default, MockHttp applies the following rate limit...</description></item><item><title>Library API</title><link>https://mockhttp.org/docs/library/</link><guid isPermaLink="true">https://mockhttp.org/docs/library/</guid><description>Parameters: options? (MockHttpOptions): port?: number - The port to listen on (default: 3000) host?: string - The host to listen on (default: &apos;0.0.0.0&apos;) autoDetectPort?: boolean - Auto-detect next available port if in use (default: true) he...</description></item><item><title>MockHttp.org Live</title><link>https://mockhttp.org/docs/hosted/</link><guid isPermaLink="true">https://mockhttp.org/docs/hosted/</guid><description>mockhttp.org is a free hosted instance of this codebase for testing. It runs on a Cloudflare Worker (no containers): documentation is served from Workers Assets, and mock APIs such as /get and /post run in the Worker. The service is globall...</description></item><item><title>Cloudflare</title><link>https://mockhttp.org/docs/how-to-deploy/cloudflare/</link><guid isPermaLink="true">https://mockhttp.org/docs/how-to-deploy/cloudflare/</guid><description>Deploy MockHTTP as a Cloudflare Worker. Workers Assets serve the docs site, and the Worker handles mock API routes.</description></item><item><title>Docker</title><link>https://mockhttp.org/docs/how-to-deploy/docker/</link><guid isPermaLink="true">https://mockhttp.org/docs/how-to-deploy/docker/</guid><description>Run MockHTTP from the jaredwray/mockhttp image, with Docker Compose, or by building the image from this repository.</description></item><item><title>Wasmer</title><link>https://mockhttp.org/docs/how-to-deploy/wasmer/</link><guid isPermaLink="true">https://mockhttp.org/docs/how-to-deploy/wasmer/</guid><description>Deploy the MockHTTP Node.js server to Wasmer Edge with app.yaml, Anybuild, and wasmer deploy.</description></item></channel></rss>
@@ -35,7 +35,7 @@
35
35
  </script>
36
36
  <title>MockHTTP - Getting Started</title>
37
37
  <script type="application/ld+json">
38
- {"@context":"https://schema.org","@type":"TechArticle","headline":"Getting Started","description":"","url":"https://mockhttp.org/","publisher":{"@type":"Organization","name":"MockHTTP"},"dateModified":"2026-10-03","keywords":[]}
38
+ {"@context":"https://schema.org","@type":"TechArticle","headline":"Getting Started","description":"","url":"https://mockhttp.org/","publisher":{"@type":"Organization","name":"MockHTTP"},"dateModified":"2026-10-20","keywords":[]}
39
39
  </script>
40
40
  </head>
41
41
 
@@ -375,7 +375,7 @@
375
375
  <a href="https://docula.org/" target="_blank" rel="noopener noreferrer" class="footer__link" aria-label="Docula">
376
376
  <svg class="footer__bat" xmlns="http://www.w3.org/2000/svg" viewBox="70 110 360 205" fill="currentColor"><path d="M420,173c-16.76-18-72.78-36.65-131.59-30.58A160.15,160.15,0,0,0,269,120.55c-3.78,4.52-11.15,9.45-19,9.45s-15.23-4.93-19-9.45a160.15,160.15,0,0,0-19.4,21.83C152.78,136.31,96.76,154.94,80,173c48.86,5.86,79.5,34.09,79.5,72.06,52.37,0,77,28,90.5,59.94C263.54,273,288.13,245,340.5,245,340.5,207,371.14,178.8,420,173Z"/><circle cx="239.19" cy="145.36" r="3.32" fill="#fff"/><circle cx="260.81" cy="145.36" r="3.32" fill="#fff"/></svg>
377
377
  </a>
378
- <span class="footer__updated">updated: 2026-10-03</span>
378
+ <span class="footer__updated">updated: 2026-10-20</span>
379
379
  </footer>
380
380
  <script src="/css/highlight/highlight.min.js"></script>
381
381
  <script>