Skip to content
Mittelware
All posts

Run your frontend locally against staging, with the headers and responses you need (no code changes)

Your frontend runs on localhost and your data lives on staging. Add the auth and tenant headers the server wants and change one response, using two Mittelware rules, without touching the code, the config or the server.

The Mittelware team 7 min read

You're building a screen. The frontend runs on your laptop at localhost:5173, and the data you need lives on the staging backend. It should be one environment variable: point the app at staging and go.

Then the page shows an error. Staging wants an Authorization token that your local login can't produce, and an X-Tenant header that in production a gateway adds for you. And the feature you're building sits behind a flag that staging has switched off.

The usual fixes all touch something: a hack in the code that adds the headers, a mock server, a change to the staging config. This post uses two Mittelware rules instead. One adds the headers to every request on its way out, and one edits a single response on its way back. Your code and the server stay as they are.

The setup#

To keep this repeatable we used a small local server standing in for staging, at http://api-staging.localtest.me:4060. It behaves like many real staging servers:

  • Every request needs an Authorization header, or it answers 401.
  • Every request also needs an X-Tenant header, or it answers 400.
  • GET /api/config returns the feature flags, with betaCheckout switched off.
  • GET /api/orders returns a list of orders.
  • It sends no CORS headers at all.

The frontend is a small dashboard on localhost:5173 that calls those two endpoints. It sends no auth headers, because on your laptop it has none.

Before you start, two things for your own setup:

  • Open the browser through Mittelware. On the Applications page, open your browser from Mittelware (the isolated option leaves your own windows alone). See Opening apps through Mittelware.
  • HTTPS needs the certificate. A real staging API is almost certainly https://. Trust Mittelware's certificate once and its traffic becomes readable: see HTTPS & certificates. Our stand-in uses plain http, so we didn't need that step.

1. See what staging says#

Open the app. This is what the page shows with no rules:

The Acme Admin dashboard with two red error cards: Couldn't load /api/config and Couldn't load /api/orders, both reading 401 Unauthorized: Missing Authorization header.
Staging rejects both calls. The app has no token to send.

The page can display that 401 message, and that's worth a pause. A browser app on localhost calling another origin is blocked from reading the answer unless the server sends CORS headers, and our server sends none. Mittelware takes care of that by default: it makes every response readable by the page. See CORS errors and Mittelware. Without it you'd be looking at a CORS error instead of the real message, which tells you nothing about what's missing.

Now open Flows and search for the staging host:

The Flows list filtered by api-staging, with two GET rows for /api/config and /api/orders, both with status 401.
Two calls, two 401s. The page on localhost itself isn't listed: browsers send localhost straight to the server.

Click a row and read the response. Staging tells you exactly what it wants: Missing Authorization header. Once that is fixed, it will say Missing X-Tenant header. We'll add both at once.

2. Rule one: add the headers#

Open Rules, click Create Rule, and fill it in:

  1. App: your browser (here Microsoft Edge). Only its traffic is touched.
  2. Label: Staging: add auth and tenant
  3. When… Domain is api-staging.localtest.me
  4. Then… choose Modify Request, click Add action, choose Headers, and add two rows, both set to Set:
    • Authorization → Bearer dev-service-token
    • X-Tenant → acme-dev
The Create Rule form: label Staging: add auth and tenant, app Microsoft Edge, a condition Domain is api-staging.localtest.me, and Modify Request with a Headers card setting Authorization to Bearer dev-service-token and X-Tenant to acme-dev.
Match the staging host, then set two headers. Every request to it gets both.

Save it and reload the app:

The dashboard now showing a Recent orders table for tenant acme-dev with five orders.
Same code, same page. The orders load.

The app didn't change. It still sends a request with no token, and Mittelware adds the headers on the way out. You can check what staging really received. In Flows, open the request and look at Headers, Request: the rule's two headers are at the bottom:

The Headers tab of the orders request, with the badge Staging: add auth and tenant executed. The last two request headers, authorization and x-tenant, are outlined.
Flows shows the request as it was sent, with the two headers the rule added.

Notice origin: http://localhost:5173 in the same list. That's the page's own address, still there, and still what staging sees.

TIP

Real tokens expire. When yours does, open this one rule, change the value and save (Ctrl + S, or Cmd + S on macOS). Every request picks up the new token straight away. It's the only place the token lives, so it never reaches your code, your config or your git history.

3. Rule two: change one response#

The orders load, but the feature you're building is behind betaCheckout, and staging has it off. The dashboard shows the new checkout panel only when /api/config says true.

Don't ask a colleague to change staging, and don't edit your code to ignore the flag. Change that one answer instead.

Modify Response replaces a response's status, headers or body. For a body, the way to "change a bit" is to start from the real one. In Flows, open /api/config, go to the Response tab and click Copy:

The Flows list with two 200 rows, and the details of /api/config with the badge Staging: add auth and tenant executed. The response shows betaCheckout false. The Copy button is marked 1.
The real response. Copy it, then change the one value you care about.

Create a second rule:

  1. App: your browser.
  2. Label: Staging: beta checkout on
  3. When… two conditions (both must match): Domain is api-staging.localtest.me, and Path is /api/config
  4. Then… choose Modify Response, set Behavior to After the server responds (the page still gets the real response, with only your change), click Add action, choose Body, paste the copied JSON and change betaCheckout to true.
The Create Rule form: label Staging: beta checkout on, conditions Domain is api-staging.localtest.me and Path is /api/config, and Modify Response with a Body card holding the JSON with betaCheckout set to true.
The whole body is replaced, so the copy keeps everything else exactly as staging sent it.

Reload the app:

The dashboard with a New checkout panel marked BETA above the orders table.
The new checkout panel appears. The orders below are still the real staging data.

Everything else on the page is still real staging data. Only /api/config was touched, because the rule's Path condition is narrow.

NOTE

A body is replaced as a whole, not edited in place. The copy is a snapshot: if staging adds a flag tomorrow, your copy won't have it until you copy again. That's fine for a small config document, which is what flags usually are. For a long list, change something smaller, such as a header or the status code, or use a mock: Mock an API before it exists.

4. Two rules, one request#

Open the /api/config request in Flows again. Both rules acted on it: the first added the headers on the way out, and the second replaced the answer on the way back.

The Flows list with the /api/config row marked by a shield, and a badge reading 2 rules matched. A list under the badge names Staging: add auth and tenant and Staging: beta checkout on.
1: the shield. 2: the badge. 3: both rules, in the order they acted.

A request can be changed on its way out and again on its way back, so a Modify Request rule and a Modify Response rule work together, in either order. For /api/orders only the first rule matches, so its badge names just that one. See Order, and when two rules act on one request.

When you're done#

Both rules have switches on the Rules page:

The Rules list with two rules, Staging: beta checkout on (Modify Response) and Staging: add auth and tenant (Modify Request), both switched on.
Switch both off and the app talks to staging exactly as it did at the start.

Switch them off and you're back to the plain 401. Switch them on tomorrow and you're back at work. Because these are ordinary rules, you can also export them to a file and give it to a teammate. Look at the file first, though: it contains the token you typed.

Things to watch for#

  • Scope tightly. The headers rule matches only the staging domain, so the token is never sent to any other host. Keep it that way: a token in a rule that matches acme loosely could leak to a different service.
  • Use a token meant for development. A short-lived or low-privilege service token is a better fit for a rule than your personal admin token.
  • Calls your dev server makes are invisible. If your dev server forwards /api to staging itself (a Vite or webpack proxy), the browser only talks to localhost, and the call to staging comes from the dev server, outside the browser. Mittelware sees what goes through its proxy, so for this recipe point the frontend straight at the staging URL.
  • Test the real login before you ship. The rule gets you working data today. It doesn't prove your login flow works, so don't leave it as the only thing you've tested.

Keep reading