API Prototypes Need Persistent Operations
API prototype operations should be able to mutate data.
API mock servers can’t mutate1 data. Most API mock servers focus on generating predictable and repeatable responses. It doesn’t matter how many times you attempt to create or modify data. The changes you think you’re making aren’t retained over time. And that is totally fine, because an API mock doesn’t need to persist information. However, if what you want is to simulate the behavior of a real API, then you need to offer persistence. In fact, persistence is one of the key characteristics of high-fidelity API prototypes. Without it, it’s very hard to simulate all the behaviors of an API. How can you create an API prototype with persistent operations, then? Keep reading.
This article is brought to you with the help of our supporter, Scalar.
Scalar is the modern OpenAPI platform for the entire API lifecycle. Govern APIs with Scalar Registry, test offline with their built-in Client, generate beautiful documentation, and ship SDKs instantly - all from your single source of truth.
You can’t create an API prototype if you can’t persist data. That’s because, without persistence, you can’t simulate any operation that mutates data. And those operations are the ones with the highest probability of generating misalignment between API designers and stakeholders. For that reason, you really want to make sure you’re simulating operations properly. Unfortunately, using regular API mock servers won’t get you there. It’s true that some API mock solutions offer persistence2. However, that’s not the norm.
Data persistence is the ability to store information provided by API consumers so it’s available across requests. In the context of API prototypes, persisting data means that information is created, updated, or removed by mutating operations, and then retrieved by reading operations. Data persistence requires a storage mechanism accessible from the engine running the API prototype. It also needs a way to interpret when and how the data should be stored or retrieved. The easiest form is to store entire payloads and delete data identified by unique URIs. However, this approach might not work well in all situations. That’s why implementing a solution that users can easily customize sounds like a better approach.
What I did when I implemented my approach of an OpenAPI-based API prototype3 was to create a system that interprets code that users can execute on each operation. I called it an x-handler4 and made it an OpenAPI attribute that you could add to any operation. The code inside an x-handler has access to the request payload as well as any input parameters. I also created a few libraries for common tasks such as filtering input data, paginating results, and generating fake information. And, obviously, storing, updating, deleting, and retrieving information. Or, in other words, a CRUD to an underlying storage, which, in my case, was simply an SQLite database. Whenever there’s a store.create() call inside an x-handler, the system will store the information inside the given store. Reading information is as simple as calling store.read() or store.list() to get a list of items. There’s also a store.update() and a store.delete() functions that let you update and delete information, respectively. It’s worth noting that Microcks follows a solution that’s similar to what I just described. My approach is more simplistic, though. After all, they’re building a whole solution while I just put together a demo of an API prototype system.
The advantage of having an x-handler can also be the biggest drawback of an API prototype system. While many people enjoy being able to add custom logic to API operations, there are others who prefer not having to do so. And, if they really have to, they prefer doing it to implement the final API, not a prototype. I totally understand. After all, if you’re going to invest time and resources in building custom operation logic, why not just implement the final version? There are other people who simply don’t care for custom logic at all. To those, using a mock server to get feedback from stakeholders is more than enough. Even data persistence itself can be a burden to maintain. What happens, for instance, to all the data that’s created during one prototyping session? Perhaps you need to reset the data store between sessions, or you might need to keep separate stores for different stakeholders. Some people simply avoid having to deal with any of these situations by not implementing data persistence.
Should you build a full API prototype with data persistence and, perhaps, more custom logic? The answer depends on what your API looks like and the amount of realism your stakeholders need to provide meaningful feedback. If your API doesn’t have mutating operations, then using a static mock server might be enough. If, on the other hand, your API makes changes to data, then you most certainly will need persistence. It’s up to you to understand if data persistence will help or if you can reach your goals without it.
Data mutation is the process of changing, modifying, or updating data in some way. A mutation is any API operation that alters the state of the data rather than just reading it. It’s the opposite of a query or read operation, which only retrieves data without changing it. The notion of data mutation was popularized with GraphQL. See GraphQL Mutations for more information.
“My goal was to convert the steps of sketching, validating, mocking, and testing an API design into a single seamless flow where I could experiment freely until satisfied,” I wrote in An Interactive Approach to API Prototyping Using OpenAPI, October 2024.
I thought of using the name x-handler to represent the custom code that can run on any operation. I used the word “handler” because the code handles the incoming request with custom logic. The prefix x- is required to identify the attribute as an OpenAPI specification extension. See the official OpenAPI specification documentation for more information.

