<?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: Gene Beal</title>
    <description>The latest articles on DEV Community by Gene Beal (@spillcoffee).</description>
    <link>https://dev.to/spillcoffee</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3974559%2Fb1ba1251-8843-4150-9f21-cc12f485501d.png</url>
      <title>DEV Community: Gene Beal</title>
      <link>https://dev.to/spillcoffee</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/spillcoffee"/>
    <language>en</language>
    <item>
      <title>From Crow's Nest to Home Port</title>
      <dc:creator>Gene Beal</dc:creator>
      <pubDate>Fri, 19 Jun 2026 15:04:49 +0000</pubDate>
      <link>https://dev.to/spillcoffee/from-crows-nest-to-home-port-2aaj</link>
      <guid>https://dev.to/spillcoffee/from-crows-nest-to-home-port-2aaj</guid>
      <description>&lt;h2&gt;
  
  
  Hauling your captured webhooks down to localhost
&lt;/h2&gt;

&lt;p&gt;In our last post, Listen and Learn, we made an unusual argument. Before you write a line of webhook-handling code, send a lookout up to the crow’s nest. Let AI spy the hooks coming in, intact, the real headers, the real payloads, the strange edge cases your provider swears do not exist.&lt;/p&gt;

&lt;p&gt;So now you’ve spotted the treasure. You know what’s coming. But a glint on the horizon isn’t gold in your hands. The next move is to haul that cargo back to your port, localhost, and crack it open against your real code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Spotting treasure isn’t holding it
&lt;/h3&gt;

&lt;p&gt;Here is what changes. You can generate your tests and mocks straight from what you captured, and the old way, the careful hand-crafting of mocks and the hoping they resemble reality, is gone. No more reading the docs three times. No more poking around the provider’s UI, re-triggering what you guess is relevant, and waiting for it to fire. You have the real payloads already. Every one of them. All that’s left is getting them down onto your machine, and that is the job the &lt;code&gt;flurryport&lt;/code&gt; CLI was built for.&lt;/p&gt;

&lt;p&gt;The shift: real captures, sent home &lt;a href="https://flurryport.io" rel="noopener noreferrer"&gt;FlurryPORT&lt;/a&gt; now ships a companion CLI to the web app. The same real requests you captured by watching your provider’s traffic back in Listen and Learn can be sent straight to your dev environment. The exact payload, the same headers, the same query parameters, forwarded to your application bit for bit, the way they first hit the FlurryPORT capture endpoint. Nothing embellished. Nothing lost on the way.&lt;/p&gt;

&lt;p&gt;Make port in 60 seconds Six steps to your first capture landing on localhost.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Install the CLI
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install -g flurryport
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Installs the global flurryport command. Run &lt;code&gt;flurryport --help&lt;/code&gt; for the full list.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Generate a token
&lt;/h4&gt;

&lt;p&gt;Log into your FlurryPORT account, go to flurryport.io/settings, generate a Personal Access Token, and copy the &lt;code&gt;fp_...&lt;/code&gt; value. It is shown once. Copy it before the dialog closes.&lt;/p&gt;

&lt;p&gt;A token is scoped to the account that made it, and to webhook replays, and nothing more. It cannot touch your projects, your targets or your billing. That makes it the thing you hand a teammate when you’re sharing an endpoint, and revoke the moment they’re done.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Log in
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flurryport login fp_your_token_here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stored once in &lt;code&gt;~/.flurryport/config.json&lt;/code&gt;. (Juggling more than one account? Use flurryport login --name alice fp_... and switch with flurryport account use alice.)&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Point a target at your local server
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flurryport target create http://127.0.0.1:4000/webhooks --name "My local"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That URL is where listen forwards every capture, so note it. The echo step has to match it exactly, same host, same port, same path. (Or run flurryport target create with no arguments and the wizard picks your project and endpoint and asks for the URL.)&lt;/p&gt;

&lt;h4&gt;
  
  
  5. Start forwarding
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flurryport listen
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It finds your localhost targets, attaches, and forwards every new capture to your machine with the original method, headers and body, until you press Ctrl+C. Anything pending from before you connected is dead-lettered, so you never replay a stale backlog on a restart.&lt;/p&gt;

&lt;h4&gt;
  
  
  6. No backend yet? Use echo
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flurryport echo 4000 --host 127.0.0.1 --path /webhooks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stands up a local server that returns 200 to everything and prints each request it gets. Run it in one terminal, flurryport listen in another, fire a test capture, and watch it run cloud to CLI to echo. The quickest way to prove the line works before you wire up real code.&lt;/p&gt;

&lt;p&gt;Once forwarding is on, hit Send on any capture in the UI and it lands on your machine with the original method, headers and body intact. That’s the whole path, end to end.&lt;/p&gt;

&lt;h3&gt;
  
  
  The mental model
&lt;/h3&gt;

&lt;p&gt;If you remember one thing, remember the rigging.&lt;/p&gt;

&lt;p&gt;The cloud is your crow’s nest, always watching, never missing an event. The CLI is the rope home. And localhost is the port where the real work actually gets done.&lt;/p&gt;

&lt;h3&gt;
  
  
  Drop anchor
&lt;/h3&gt;

&lt;p&gt;In Listen and Learn, you stopped coding blind. This is the other half. Stop testing blind. Real events, on your real code, on demand.&lt;/p&gt;

</description>
      <category>api</category>
      <category>productivity</category>
      <category>testing</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Listen and learn</title>
      <dc:creator>Gene Beal</dc:creator>
      <pubDate>Tue, 16 Jun 2026 17:29:02 +0000</pubDate>
      <link>https://dev.to/spillcoffee/listen-and-learn-2ncl</link>
      <guid>https://dev.to/spillcoffee/listen-and-learn-2ncl</guid>
      <description>&lt;p&gt;In the past when starting a project against a well documented API I would scour the docs and cherry pick the services and webhooks that I felt were most relevant for my application. My goal would be to touch the smallest subset of features necessary and get out of external code quickly, and focus on that which I controlled — not anymore.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Subscribe to everything
&lt;/h2&gt;

&lt;p&gt;Day one, I would write nothing. I would use &lt;a href="https://flurryport.io" rel="noopener noreferrer"&gt;FlurryPORT.io&lt;/a&gt; as a design-time tool and capture every event the provider offers or every event I could. For an active Shopify store it would be good to figure out the cadence and habits of those currently curating the store. You will be able to see which fields the store actually uses, where relevant data comes from, and which fields sit empty — finding the gaps the store isn’t using yet and your application can.&lt;/p&gt;

&lt;p&gt;I would also suggest, during the design phase, that you point every provider you’re integrating at the same capture endpoint, so you see true interleaved sequences of events your application will encounter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: build collections
&lt;/h2&gt;

&lt;p&gt;After a reasonable snapshot of the shop’s webhook traffic is established, I would begin to categorize and place relevant payloads into collections. This may be a daunting task as you may find the noise coming from your subscriptions to be more than you want to handle. If only we had a tool to help with this captured mess of payloads …&lt;/p&gt;

&lt;p&gt;Luckily for us all, we have access to AI. A less advertised feature of FlurryPORT is ‘Copy for AI’. Select a set of your captured traffic and click on ‘Copy for AI’ to receive a pageable preview of your payloads with a nifty ‘Copy all’ button.&lt;/p&gt;

&lt;p&gt;Pressing ‘Copy all’ will place into the clipboard of your machine a list of the captures you wish to analyze with the friendly AI companion of your choosing.&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2Frvea1sy1r3ayz04aszfo.png" class="article-body-image-wrapper"&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%2Farticles%2Frvea1sy1r3ayz04aszfo.png" alt="Screenshot of 'Copy for AI'"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is the option to scrub PII from the captured events prior to copying them for AI, with the preview window providing a sample of what will be redacted. Redaction is best-effort, not a guarantee that all PII is redacted — it would be prudent to place this in a simple text editor to verify redaction is complete prior to sending the captured events to your favorite AI.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Or, if FlurryPORT’s PII scrub is misbehaving, you can toggle it off and handle it yourself.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;`&lt;/p&gt;

&lt;h2&gt;
  
  
  Captured Webhooks (11 requests)
&lt;/h2&gt;




&lt;h3&gt;
  
  
  1. Unknown (99 B) — Unknown
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Captured&lt;/strong&gt;: 2026-06-07 19:02:38 UTC&lt;/p&gt;

&lt;h4&gt;
  
  
  Headers
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;http&lt;br&gt;
Accept: */*&lt;br&gt;
Host: localhost:8083&lt;br&gt;
User-Agent: curl/8.15.0&lt;br&gt;
Content-Type: application/x-www-form-urlencoded&lt;br&gt;
Content-Length: 99&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Body
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;plaintext&lt;br&gt;
captain=Redbeard&amp;amp;payout_card=[REDACTED:CREDIT_CARD]&amp;amp;clerk_email=[REDACTED:EMAIL]&amp;amp;clerk_phone=[REDACTED:PHONE]&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  2. Unknown (150 B) — Twilio
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Captured&lt;/strong&gt;: 2026-06-07 19:02:38 UTC&lt;/p&gt;

&lt;h4&gt;
  
  
  Headers
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;http&lt;br&gt;
Accept: */*&lt;br&gt;
Host: localhost:8083&lt;br&gt;
User-Agent: curl/8.15.0&lt;br&gt;
Content-Type: application/json&lt;br&gt;
Content-Length: 150&lt;br&gt;
X-Twilio-Signature: sq==&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Body
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;json&lt;br&gt;
{&lt;br&gt;
  "type": "desertion.alert",&lt;br&gt;
  "deserter": "Yellow-Belly Jim",&lt;br&gt;
  "last_email": "[REDACTED:EMAIL]",&lt;br&gt;
  "last_phone": "[REDACTED:PHONE]",&lt;br&gt;
  "bounty_ssn": "[REDACTED:SSN]"&lt;br&gt;
}&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Unknown (174 B) — Unknown
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Captured&lt;/strong&gt;: 2026-06-07 19:02:37 UTC&lt;/p&gt;

&lt;h4&gt;
  
  
  Headers
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;http&lt;br&gt;
Accept: */*&lt;br&gt;
Host: localhost:8083&lt;br&gt;
User-Agent: curl/8.15.0&lt;br&gt;
Content-Type: application/xml&lt;br&gt;
Content-Length: 174&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Body
&lt;/h4&gt;

&lt;p&gt;&lt;code&gt;&lt;/code&gt;&lt;code&gt;xml&lt;br&gt;
&amp;lt;lookout&amp;gt;&amp;lt;spotter&amp;gt;Crow Nest Carl&amp;lt;/spotter&amp;gt;&amp;lt;email&amp;gt;[REDACTED:EMAIL]&amp;lt;/email&amp;gt;&amp;lt;enemy_ipv6&amp;gt;[REDACTED:IPV6]&amp;lt;/enemy_ipv6&amp;gt;&amp;lt;enemy_ipv4&amp;gt;[REDACTED:IPV4]&amp;lt;/enemy_ipv4&amp;gt;&amp;lt;/lookout&amp;gt;&lt;br&gt;
&lt;/code&gt;&lt;code&gt;&lt;/code&gt;&lt;br&gt;
... Truncated for brevity`&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3: Replay and develop your application
&lt;/h2&gt;

&lt;p&gt;Once settled on a series of events relevant to your application — organized by AI — you can now place your chosen events into a collection to be used during your development lifecycle. FlurryPORT offers a variety of options to help you scope your work and get any event to the correct endpoint of your application.&lt;/p&gt;

&lt;p&gt;&lt;a href="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%2Farticles%2F978nh4rks6kvmhqqd268.png" class="article-body-image-wrapper"&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%2Farticles%2F978nh4rks6kvmhqqd268.png" alt="Screenshot of captured payloads"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;FlurryPORT's model is “everything arrives; bindings decide what it looks like when it does.” Here is how it works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; Add a replay target to a capture endpoint and it receives every event — delivery is never suppressed. A bare target with no bindings is the raw firehose, forwarded byte-for-byte.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;NOTE: localhost targets that are not connected won’t receive events fired before they connect — this is by design, to mimic how your real-world server would behave if it were offline.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Shaping is a standing route: each binding pairs a JSONata predicate with a transformation, scoped to that endpoint-and-target. Predicates are evaluated against the original capture — body, headers, query — and each match queues its own replay, pinned to its transformation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Transformation runs last, at send time, so each target gets the payload reshaped for it — trimmed to the three fields a tracking service needs, or restructured to a manufacturer’s schema.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;When nothing matches, the capture forwards as-is. The system routes and reshapes; it never drops. If your predicate is mistuned you get the raw event, not silence.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Want to try the listening half of this today? Paste an endpoint from &lt;a href="https://flurryport.dev/try" rel="noopener noreferrer"&gt;flurryport.dev&lt;/a&gt; into any provider's webhook settings — no account, captures live for about an hour. The design phase starts with hearing what your store actually says. One caution around the flurryport.dev endpoints: this demo endpoint does not apply envelope encryption to captured events until they’re imported into an account.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webhook</category>
      <category>startup</category>
      <category>beginners</category>
      <category>architecture</category>
    </item>
    <item>
      <title>The orders/updated firehose</title>
      <dc:creator>Gene Beal</dc:creator>
      <pubDate>Wed, 10 Jun 2026 15:51:03 +0000</pubDate>
      <link>https://dev.to/spillcoffee/the-ordersupdated-firehose-29fg</link>
      <guid>https://dev.to/spillcoffee/the-ordersupdated-firehose-29fg</guid>
      <description>&lt;p&gt;&lt;strong&gt;I only listened when I was the one talking&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In the last post I described setting up Shopify order fulfillment for a former colleague’s store — the undocumented partner API, the offshore go-live, the request to test through production. This is the part of the story I’m less proud of.&lt;/p&gt;

&lt;h2&gt;
  
  
  Confession: I turned off orders/updated
&lt;/h2&gt;

&lt;p&gt;When I designed the system I subscribed to the webhook topics that seemed obviously necessary: order created, order updated. I designed an idempotent store with retry logic to make a durable handoff to product manufacturing. During development and test phases I felt I had a tight reliable system with zero holes.&lt;/p&gt;

&lt;p&gt;Here’s why it was tidy, as best I can reconstruct it: I only started my local listener when I was ready to submit an order from the test store. Listen, fire the order, verify, stop. Any edits I made to orders outside those windows happened while nothing was listening. Did the test store send me updates outside of my expected happy path? I really don’t know. My dev workflow was structurally incapable of showing me the noise, because I only listened when I was the one talking.&lt;/p&gt;

&lt;p&gt;Production listens all the time. The moment I started monitoring live traffic for the store my system heard everything. I learned what orders/updated actually means. It doesn’t mean “something you care about changed.” It means anything changed: a fulfillment status ticked over, a note was edited, a tag was added, a payment state shuffled internally. One real order produced a flurry of updates, each one a fresh chance for my idempotency logic to misclassify a duplicate as new. If only my dev environment listened all the time.&lt;/p&gt;

&lt;p&gt;My heart sank and I scrambled for a solution.&lt;/p&gt;

&lt;p&gt;So I did the thing you do at 11 p.m. with production live: I scaled the subscription back to orders/create only, and promised myself I’d deal with the noise properly later. The pipeline stayed accurate. The promise stayed unkept.&lt;/p&gt;

&lt;h2&gt;
  
  
  The actual problem: every event goes to one handler at full volume
&lt;/h2&gt;

&lt;p&gt;The mistake wasn’t subscribing to &lt;em&gt;orders/updated&lt;/em&gt;. There is real value in a system that can monitor updates and self-correct when/if an order changes. The mistake was an architecture where every event Shopify emits lands on one handler at full fidelity, and that handler was not smart enough to sort signal from noise. In product manufacturing a classification bug becomes a fulfillment bug.&lt;/p&gt;

&lt;p&gt;What I wanted was a layer in front: something that captures everything (so no data is lost), lets me see the noise before writing code against it, and forwards each downstream service only what it needs, in the shape it needs. A capture endpoint is also always listening. One could point the dev store at one on day one, and every event fires into it whether or not your local handler is running. The firehose would have shown up in week one of development instead of night one of production.&lt;/p&gt;

&lt;p&gt;Shopify has since given developers includeFields and delivery filters, and you should use them. They are precision tools you can use to optimize your system and receive only what you care about. But you may only care after you've seen your own traffic. I needed something that showed me everything first at design time, or I needed the capability to filter without intrusive hacks to my final system. With &lt;a href="https://flurryport.io" rel="noopener noreferrer"&gt;FlurryPORT&lt;/a&gt;, if you filter out too much you could change your filter and replay it again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Transform in flight, route per target
&lt;/h2&gt;

&lt;p&gt;That’s the other half of FlurryPORT. Captures can be reshaped with a JSONata transformation and fanned out. The same webhook can go to multiple endpoints of your application, each receiving the payload as-is or reshaped by its own JSONata transformation. One webhook in, a differently-shaped copy out to each target that needs it. In my fulfillment setup, that looks like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;orders/create&lt;/em&gt; → transformed to the manufacturer’s schema → fulfillment endpoint.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;em&gt;orders/updated&lt;/em&gt; → stripped to the three fields I actually cared about → a separate tracking service that updates status and nothing else. Or, just sit and record each orders/updated notification and leave the collection of notifications as data for the next iteration of the project.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And it solves the test-traffic problem from my previous article. When you replay a captured production order at a test pipeline, the transformation can rewrite it in flight by injecting a test flag, zeroing the quantity, rewriting the SKU to a sandbox value. Mutate a real order so that if a replay somehow reached a manufacture queue it would carry ample evidence it was a test.&lt;/p&gt;

&lt;p&gt;One more layer of protection worth mentioning: FlurryPORT validates inbound signatures before storing anything, rejecting invalid requests with a 401 and flagging the attempt. A public webhook endpoint that triggers manufacturing is exactly the endpoint you want verifying every sender.&lt;/p&gt;

&lt;p&gt;The lesson I’d spill for anyone building fulfillment on webhooks: subscribe wide, but never let raw provider events touch the system that spends money. Capture first, transform deliberately, route narrowly.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to watch your own firehose before deciding what deserves to drink from it? Paste an endpoint from &lt;a href="https://flurryport.dev/try" rel="noopener noreferrer"&gt;flurryport.dev&lt;/a&gt; into your provider’s webhook settings — no account, nothing to install, captures live for about an hour. The free tier includes a transformation, enough to put one between your events and anything that spends money.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webhooks</category>
      <category>shopify</category>
      <category>sdlc</category>
      <category>testing</category>
    </item>
    <item>
      <title>"Can you send some test requests through production?"</title>
      <dc:creator>Gene Beal</dc:creator>
      <pubDate>Mon, 08 Jun 2026 17:01:37 +0000</pubDate>
      <link>https://dev.to/spillcoffee/can-you-send-some-test-requests-through-production-3fpp</link>
      <guid>https://dev.to/spillcoffee/can-you-send-some-test-requests-through-production-3fpp</guid>
      <description>&lt;p&gt;&lt;strong&gt;One webhook in, every dock served.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A while back I set up order fulfillment for a former colleague’s Shopify store. On paper it’s the well-trodden path: read the docs, subscribe to the webhook topics, write a service that stores incoming orders idempotently, hand them off to the manufacturer’s system.&lt;/p&gt;

&lt;p&gt;In practice it was the other kind of integration. The fulfillment side had an undocumented API, so a good chunk of the project was reverse-engineering why my requests weren’t firing and negotiating with their offshore dev team to reset state on their end so I could verify my responses were actually landing. Wanting to be professional, I went out of my way to provide in-depth Swagger docs for the interface to communicate fulfillments back through my services to Shopify. I stood up two environments, one for end-to-end testing from a Shopify test store through a development environment I insisted be set up and a return trip for shipping notifications.&lt;/p&gt;

&lt;p&gt;So far, so professional. Then the offshore team went live.&lt;/p&gt;

&lt;p&gt;Their go-live verification plan was for me to push test orders through the production pipeline. The pipeline that ends in an actual manufacturing queue. The request was to mutate the purchase order id to prevent actual builds of the product from being made; a request never negotiated during the development lifecycle on either end. For a project priced for a friend the extra development to accomplish this was out of scope, “manufacture some test products so we can check our side”. Not to mention sending test data flowing through a production environment made my skin crawl.&lt;/p&gt;

&lt;p&gt;Their instinct wasn’t wrong. They wanted to verify the system with real-shaped traffic, not synthetic fixtures. They probably had production systems that they wanted to see exercise the entire data pipeline. My system, built on a budget, had only one input mechanism and that was an order from the shop it was installed on. Forging a fake one to go through the system end to end would mean faking HMAC signatures and hand verifying every property. If only there was some middleware that could sit between my system and theirs.&lt;/p&gt;

&lt;p&gt;What everyone actually wanted was: take real captured traffic, point it at a non-production endpoint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Capture once, replay anywhere&lt;/strong&gt;&lt;br&gt;
That’s the problem &lt;a href="https://flurryport.io" rel="noopener noreferrer"&gt;FlurryPORT.io&lt;/a&gt; exists to solve. You put a capture endpoint between your provider and your handler or your handler and an external destination. Every webhook gets stored exactly as sent. Poorly documented endpoints become self-documenting; success and failures are recorded as you debug. Replay to your heart’s content to localhost, test/qa, staging or a remote team with a poorly documented API.&lt;/p&gt;

&lt;p&gt;Because the replay is byte-for-byte, the X-Shopify-Hmac-Sha256 signature still verifies on the receiving end. Which means I could send any production order to my test environment, skip mimicking an order from Shopify, add some test indicator to the outgoing request and send it on to the manufacturer’s production environment for their acceptance testing. No mocking, no fixtures drifting out of date, no commenting out the verification “just for testing.”&lt;/p&gt;

&lt;p&gt;Two features matter specifically for go-live verification:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sequential replay.&lt;/strong&gt; Real verification isn’t one webhook, you are replaying a sequence. An order created, then updated, then cancelled; did you receive the update before the create (a classic Shopify quirk)? FlurryPORT replays a selected sequence in exact order, waiting for each request to complete, stopping at the first failure. That’s an end-to-end pipeline test built from traffic that actually happened.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Replay without re-triggering.&lt;/strong&gt; Captures persist. You can replay the same order a hundred times while debugging the fulfillment handoff without asking anyone to place another order, bumbling through a test shop to send the same order again, or in my case, without negotiating another state reset with a dev team nine time zones away.&lt;/p&gt;

&lt;p&gt;If I’d had this during that project, the go-live conversation becomes: “I’ll replay last Tuesday’s real orders at your test endpoint. Tell me when you’re ready.” No production traffic, no manufactured test products, no scope creep.&lt;/p&gt;

&lt;p&gt;The remote team could have also been connecting to a FlurryPORT endpoint of their own or one you supplied; to bring clarity to what they are sending and receiving from your service.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want to see a capture happen before reading any more marketing? Paste an endpoint from &lt;a href="https://flurryport.dev/try" rel="noopener noreferrer"&gt;flurryport.dev&lt;/a&gt; anywhere a webhook goes — no account, nothing to install, captures live for about an hour. When you're ready to replay them at your own pipeline, the free tier covers a project, two endpoints, and three days of retention.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>programming</category>
      <category>devops</category>
      <category>webhooks</category>
    </item>
  </channel>
</rss>
