← Back to field notes
Infrastructure6 min readSep 24, 2026

Curl First, Build Second: The API Validation Methodology

The one rule that shaped every feature in MistPulse: never write application code against an API response shape you haven't verified yourself, with a real curl call, first.

Part 3 of 10 in Building MistPulse: A Network Architect's Field Notes

Share:XLinkedIn
Curl First, Build Second: The API Validation Methodology

If there's a single sentence that summarizes how MistPulse got built, it's this: never write application code against an API response shape you haven't verified with your own eyes. Every feature in this app, from the first SLE dashboard to the most recent endpoint migration, started the same way. Before a single line of Swift or TypeScript was written, I ran a real curl command against the real Mist API, with real (if scoped-down) test data, and read the actual JSON that came back.

Where the discipline came from

This wasn't a methodology I adopted for a blog post. It came from years of being burned by documentation. API docs are written at a point in time, by people who are also juggling the product roadmap, and they drift from reality: a field gets renamed, a nested object gets flattened, a numeric value quietly becomes a string. Juniper's Mist API documentation is better than most, but "better than most" still isn't a guarantee. The only source of truth for what an endpoint actually returns, right now, for your data, is the endpoint itself.

The only source of truth for what an endpoint returns is the endpoint itself, not the page describing it.

So the workflow became routine: pick the endpoint, construct the curl call with the right headers and scoped test parameters, run it, and read the response line by line before writing a single TypeScript interface or Swift struct to model it. Only once I could describe the shape of the real response, which fields are always present, which are optional, what the empty-state response looks like, what an error actually returns as opposed to what the docs claim it returns, did I let myself start building.

The curl-first methodology loop: pick the endpoint, curl it, read the real response, then build the model

A concrete example: testing before trusting

A good illustration of this discipline in practice, without getting into specifics of any one endpoint, is how I handled edge cases. It's not enough to test the happy path: a device that exists, a site with active clients, a time window with data. I made a habit of deliberately testing the boundaries: what does the API return for a site with zero devices? What happens when you request a metric for a time window that's entirely in the future, with no data possible? Does an invalid ID return a 404, or does it return a 200 with an empty body, or nulls?

Those answers matter enormously for how you write the client code that consumes them. A team that assumes "no data" always means a 404 will write error-handling code that breaks the moment an endpoint instead returns 200 with null values, which, in more than one case with the Mist API, is exactly what happens. Catching that with a curl command costs thirty seconds. Catching it after it ships as a confusing blank screen for a real user costs a lot more.

  • Test the happy path, but never stop there
  • Test the empty-state response, not just the populated one
  • Test malformed or nonexistent IDs, and note whether you get an error status or a silent empty success
  • Save the raw JSON responses somewhere so they can be diffed against later, when the API changes

Tooling: a home lab as the safe place to test

Running live tests against production infrastructure APIs requires somewhere safe to point them. I used my own home network, a small but real Juniper Mist deployment with its own sites, switches, and access points, as the sandbox. Having a remote tool that could reach into that home lab environment and run curl commands directly meant I could validate an API response shape in the same few minutes it took to think through a feature, rather than context-switching to a separate test environment or waiting on shared infrastructure.

This also meant that every single feature in MistPulse has been checked, at least once, against a real device or a real site, not a mocked response, not an assumption carried over from documentation. It's slower than trusting the docs and writing code straight away. It has also meant that, across the entire life of this project, I can count on one hand the number of times a shipped feature broke because the API didn't behave the way I expected. Nearly every surprise got caught at the curl stage, where fixing it costs nothing.

Where this discipline gets tested hardest

The real payoff of "curl first" doesn't show up on the features you build calmly, with time to spare. It shows up when something changes underneath you: when Juniper deprecates an endpoint pattern, or tweaks a response shape, and you have to migrate a feature that's already in production without breaking it for real usage. That's a story with its own post later in this series, and it's the clearest demonstration I have of why this methodology earns its keep: when the pressure is on to ship a fix fast, having a discipline that forces you to verify before you code is what keeps "fast" from turning into "reckless."

Before we get there, though, there's a more basic truth about building software solo: even with the best methodology, bugs happen. The next post is about the real ones, not the abstract kind, but the specific mistakes that made it into early builds of MistPulse, and what fixing them taught me.

Coming up next: Real Bugs, Real Fixes: the debugging stories from early builds, and how a solo project handles its own QA

J

Written by

Jesus Hurtado

Network Architect and Cybersecurity Professional.

Comments

No comments yet. Be the first.

Leave a comment