One components.schemas entry per data type (reverse-mapping schema_json back into OpenAPI properties - the inverse of OpenAPI Import's own mapping, sharing the same format lookup table so the two directions can never silently drift apart), and one paths entry per configured mock route. Reflects the current route/type configuration, not the master template's actual generated data. Contains only what the Runtime API itself exposes - no Spaces, members, API keys, or plan/billing information. An empty Project (no types/routes yet) still returns a valid, empty document.
Example response (abridged)
{
"openapi": "3.0.0",
"info": {
"title": "My Project Mock API",
"version": "1.0.0"
},
"paths": {
"/users": {
"get": {
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/User"
}
}
}
}
}
}
}
},
"/users/{id}/posts": {
"get": {
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "number"
},
"description": "Resolves against User.id (relationship id 1)."
}
],
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Post"
}
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"User": {
"type": "object",
"required": [
"email"
],
"properties": {
"id": {
"type": "number"
},
"email": {
"type": "string",
"format": "email"
}
}
}
}
}
}
Always lossless for anything this service's own Data Types could produce, since export only ever needs to invert its own vocabulary:
string/number/boolean/array/objectmap directly onto the same OpenAPI type names; adatefield becomes{ type: "string", format: "date-time" }.enumpasses through unchanged.min/maxon a numeric field becomeminimum/maximum.- A
formatwith a genuine OpenAPI standard equivalent (onlyemailanduuid- the same two OpenAPI Import maps in the other direction) keeps thatformatkeyword. Every other format this service supports (firstName,phoneNumber,paragraph, ...) has no OpenAPI standard equivalent, so it becomes adescriptionnoting the generator vocabulary instead of a fabricatedformatvalue an external consumer might misinterpret. requiredis collected per nesting level into the OpenAPIrequiredarray, exactly mirroring howvalidateRecordAgainstSchemaenforces it.
A route's own record-lookup parameter (the path's trailing {param}) documents which field it resolves against and that field's actual schema type. A nested route's parent parameter additionally names the relationship it depends on (project_relationships informs the parameter's description, per api-specification.md §25 - it doesn't need its own OpenAPI construct beyond that). A GET on a collection path (no record lookup) returns an array response schema; every other method - including a collection-path POST, which creates one record - returns a single-record schema, never an array (a real bug caught by exporting an actual Project and inspecting the response: a first pass wrapped every collection-path method's response in an array, including POST).