Skip to content
routebase
API Mocking10 chapters

Chapter 04 of 10

One Endpoint, Many Answers

How to get a valid case, a not-found and a validation failure out of one route without a rule per outcome, what to branch on, and which fallback belongs at the end.

A real endpoint has more than one answer. It returns the record, or a not-found, or a validation failure, and which one you get depends on what was in the request. A mock that returns the same 200 regardless is teaching the client that only one of those exists.

The wrong way to get variety

The obvious fix is a rule per outcome, and it stops working at about three outcomes.

Every variant duplicates the path, the method and usually most of the body. Every new variant has to be inserted at the right point in the priority order, and the order is now load-bearing in a way nobody wrote down. Worst of all, the rules match on the same criteria, so the only thing distinguishing them is their position, which makes a drag in the wrong direction a silent behaviour change.

Conditions solve the same problem inside a single rule. The route is declared once, the variants sit under it in an explicit order, and the rule's own response is the fallback when none of them applies.

What is worth branching on

Four sources cover nearly everything, and each one corresponds to a class of behaviour a client has to handle.

A path parameter separates the ids that exist from the ids that do not. This is the single most valuable condition to configure, because unknown identifiers are the most common real failure and the least commonly mocked one.

A body field is how validation becomes testable. A quantity over a limit, a missing required field or a malformed date can each return the documented 4xx instead of the cheerful success that a generated mock hands out.

A query parameter covers filters, paging and sorting, where the interesting answers are the empty result and the last page rather than the first one.

A header covers version negotiation and feature flags, though a credential header usually belongs in matching rather than in a condition, since the whole rule differs rather than just the body.

Conditions combine, and the combination mode matters. Requiring all of them is the safe default for describing one precise situation, while matching any of them is for grouping several inputs that should produce the same answer.

Which answer belongs at the bottom

The fallback is the response you get when nothing matched, and choosing it is a design decision rather than a leftover.

For a collection endpoint the fallback is normally the success, because listing is the ordinary case and the variants are filters. For a single-resource endpoint the fallback should usually be the 404, with conditions promoting the handful of ids your data actually contains. That inversion is the one that makes a client honest, because the developer is then forced to handle the not-found on day one instead of discovering it after the backend arrives.

The same logic applies to writes. Let the default be the documented failure and let a well-formed body earn the success, and the error path gets built while somebody is still looking at that screen.

Keep the set small

Conditions are cheap to add and each one is a line of behaviour somebody has to maintain, so the set is worth pruning.

The test is whether the variant corresponds to a state the client has to render. A 404, an empty list, a validation failure and a conflict each earn their place because each one is a different screen. A second success variant that differs only in which fields are populated usually does not, since the client renders both the same way and the variety belongs in the data instead.

In Routebase

Every rule has a Conditional Responses section, and each entry there is a set of conditions plus the response it overrides to.

A condition takes a source of query parameter, request body, header, path parameter or HTTP method, a path written in dot notation for nested body fields such as user.address.city, an operator and a value. The operators cover equality, containment, prefixes and suffixes, regular expressions, existence and numeric comparison, so a quantity above a threshold and a missing field are both expressible without a script.

Conditions inside one entry combine with Match ALL or Match ANY. The entry's Response Override replaces the status code, the content type and the body, and anything left empty falls back to the rule's own response. A typical single-resource rule ends up as a 404 by default with one condition promoting the ids in your fixtures to a 200, which is the shape the Mock Server guide walks through.

Frequently asked questions

How do you return different mock responses for the same endpoint?

Put the variants inside one rule as conditions on the request rather than spreading them over several rules. Each condition looks at a query parameter, a body field, a header, a path parameter or the method, and the first one that matches overrides the status code and the body. The rule's own response stays as the fallback for everything that matched nothing.

How do you mock a 404 for an unknown ID?

Invert the default. Make the rule's own response the 404, then add a condition that recognises the one or two ids your fixtures actually contain and overrides them to a 200 with the record. Every other id falls through to the not-found, which is what a real API does and what a mock returning 200 for any id never teaches the client.

Should you use one rule per response or conditions inside one rule?

Use conditions when the variants share a route and differ by what is in the request, because that keeps them in one place and removes the ordering question. Use separate rules when the variants differ by something the matcher already understands, such as the presence of a credential header, since that belongs in matching rather than in a branch.

Can a mock respond based on the request body?

A capable one can, and it is the only way to exercise validation. A condition that reads a field in the posted body with dot notation lets a quantity above a threshold return the 422 your contract documents. The client's error handling is then built against a response it will actually receive.

Last reviewed by The Routebase Team.

Ready to ship on it?

Routebase is live. Design your API once — docs, mocks, tests, and monitoring all follow from the same source.

14-day Pro trial — no credit card required.