Standard GraphQL-over-HTTP: request body is { query, variables?, operationName? }, response is GraphQL's own { data, errors } envelope (not this service's usual apiError shape). A read-permission API key's schema has no Mutation type at all - a mutation attempt fails as an unknown-operation schema error, not a runtime 403.
Request
{
"query": "{ users { id name } }"
}
Response
{
"data": {
"users": [
{
"id": 1,
"name": "Ada"
}
]
}
}
Every project_routes row becomes exactly one root Query/Mutation field, named deterministically from the type's routeName (and, for a nested route, the parent type's routeName) - hyphens and any other GraphQL-unsafe characters are replaced with underscores.
| Route shape | Field name | Args |
|---|---|---|
GET /literal | <routeName> | none → [Type] |
GET /literal/{'{'}id{'}'} | <routeName>_by_<lookupField> | <lookupField>! → Type |
GET /parent/{'{'}id{'}'}/child | <childRouteName>_by_<parentRouteName> | <parentRouteName>Id! → [Type] |
GET /parent/{'{'}id{'}'}/child/{'{'}id{'}'} | <childRouteName>_by_<parentRouteName>_<lookupField> | parent id + lookup field → Type |
POST (collection) | create_<routeName> (nested: create_<child>_for_<parent>) | input: <Type>Input! |
PUT (record) | replace_<routeName> | id args + input: <Type>Input! |
PATCH (record) | patch_<routeName> | id args + input: <Type>PatchInput! (all fields optional) |
DELETE (record) | delete_<routeName> | id args → Boolean |
Type mapping: string/date → String, number → Float, boolean → Boolean, array → a GraphQL list, object → a synthesized named object type. enum fields keep their underlying scalar type rather than becoming a real GraphQL enum (this service's enum values aren't all valid GraphQL enum-value names) - enum membership is still enforced server-side, exactly as REST enforces it today. A route with requiresAuth checks the same Authorization: Bearer <token> Runtime Auth issues, per field.