Mock any API style
This is a proposal - a design under discussion, not yet shipped. It may change or be dropped. It describes where Akuma is headed and why.
The thesis
Most mock-server tools assume your API is textbook REST: resources, integer ids, and verbs that map one-to-one onto HTTP methods. Real teams don't always live there. They integrate with RPC-style services, action endpoints, partner APIs that POST to delete, identifiers that are UUIDs or slugs, and - more often than anyone admits - legacy SOAP services that speak XML over a single endpoint.
Akuma's bet: mock the API you actually have, not the one a style guide wishes you had. One config format that can describe RESTful, non-REST, and SOAP services equally well is a real differentiator - and the reason these proposals exist.
What's hardcoded today
The current request handler infers everything from HTTP conventions, which is exactly what blocks the cases above:
- The action is derived from the method -
GETreads,POSTcreates,DELETEdeletes. You cannot declare aPOSTthat deletes. - An item is always found by an integer column literally named
id(WHERE id = ?), and the path must contain the literal:id. - The key is parsed as an integer, so a
:guidor:slugis rejected before it reaches the database.
The proposed engine
Rather than special-case each style, we propose decoupling the three decisions an endpoint really makes, so any style is just a combination of them:
1. Operation (decoupled from method)
An endpoint declares what it does to the store - list, get, create, update, delete, or query - independent of the HTTP method. A sensible default is still inferred from the method so RESTful configs stay terse, but it is always overridable.
# inferred (RESTful, terse)
- { path: /users, method: GET, response: { type: array, schema: User } }
# explicit override: a POST that deletes
- path: /users/:guid/archive
method: POST
operation: delete
key: guid
response: { schema: User }
status: 204 2. Key (any column, any type)
The lookup column defaults to the path-parameter name - /users/:guid looks up WHERE guid = ?, /posts/:slug uses slug - and the value is bound as text or number with no forced integer parse. An explicit key: overrides it when the URL parameter and column names differ.
- path: /posts/:slug
method: GET
response: { schema: Post } # WHERE slug = :slug, bound as text 3. Response shape
Today every response is JSON. To reach SOAP we envision an XML response shape (envelopes, faults) selected per endpoint, so the same data model can be served as JSON or XML.
The three styles
- RESTful resources - the conventional case, formalized on the engine.
- Non-REST & RPC - verbs in the path, action endpoints, custom operations, inline queries.
- SOAP & XML - XML envelopes and operation routing for legacy services.
For what is actually built today, see Endpoints and Mutations.
Open questions
- Is
operation:always inferred-with-override, or should some styles require it explicitly? - How do non-
idkeys interact with relationships and foreign-key seeding? - Do we need composite keys / multiple path params (e.g.
/orgs/:orgId/users/:guid) in v1? - How far does the engine go before an escape hatch (raw
query:) is the right answer?