GraphQL
A captured realm may declare one GraphQL source with graphql: one fixed HTTPS endpoint and a
fixed set of persisted queries. Synth writes it to graphql/operations.yml, the only path the
host reads.
credentials: { "catalog-token": { kind: "bearer", description: "A read token for the product catalog." },},graphql: { name: "catalog", endpoint: "https://api.example.com/graphql", auth: "bearer", credential: "catalog-token", operations: { productById: { document: "query($id: ID!) { product(id: $id) { name price } }", variables: [{ name: "id", type: "string", required: true, maxLength: 64 }], }, },},A handler calls each operation under the source’s name, passing only variables, and gets back
the response’s data member:
import type { ProductHandler } from "../.embabel/realm.d.ts";
interface ProductData { product: { name: string; price: number } | null;}
export const product: ProductHandler = async (input, ctx) => { const data = (await ctx.gateway.catalog.productById({ variables: { id: input.id }, })) as ProductData; return { product: data.product };};The generated types give each operation an argument typed from its declared variables. A
required variable is a required field, an optional one may be left out or null, an enum
becomes a union of its values, and a variable the operation does not declare is a compile
error. An operation with no variables takes no argument, or {}. The result is unknown, so
narrow it before use.
| Field | Rule |
|---|---|
name |
The gateway namespace: [A-Za-z_][A-Za-z0-9_-]{0,63}. |
endpoint |
One fixed https:// origin on the default port, with no user info, query, fragment, . or .. segment, percent-encoded path or { placeholder. |
credential |
A credential the realm declares under credentials. Naming it here counts as a reference. |
auth |
"bearer", sent in Authorization, or "api-key", which needs header. |
header |
"api-key" only: the header the key travels in, [A-Za-z][A-Za-z0-9-]{0,127}, and not one the host sets itself (Authorization, Cookie, Host, Idempotency-Key and the like). |
headers |
Up to 16 fixed headers, each named X-..., used once and never the key’s own header, holding at most 1,024 printable ASCII characters with no ${ substitution. |
operations |
1 to 32 persisted queries, keyed by operation name, [A-Za-z_][A-Za-z0-9_-]{0,63}. |
operations.<name>.document |
The query text, at most 16 KiB. |
operations.<name>.variables |
Up to 32, each {name, type, required?, minLength?, maxLength?, enum?}. type is string (GraphQL String or ID), integer (Int), number (Float) or boolean. minLength and maxLength are whole numbers from 0 to 2048, and enum holds up to 128 strings, finite numbers or booleans. |
What synth checks, which is also what the host refuses at load:
- Each document is exactly one
query. A mutation, subscription, fragment,...spread,__schemaor__typeintrospection, a second operation or a variable default is refused. - The document’s variable signature matches the
variableslist exactly: the same names, each once, each$name: Scalarwith!exactly whererequiredis true. A list type such as[String!]is refused. - An operation’s call names (
<name>.<operation>,<name>_<operation>, and the two camel-cased and joined with_) may not collide with an API operation’s, and may not be a host call’s own name such aschannel_publishorcypher_query. The host refuses every operation in the realm over one such clash. graphql/operations.ymlis at most 64 KiB.
A query sends the secret the owner bound to the credential and is refused while nothing is bound. The host-side rules, including the checks on each supplied value and on the response, are in the captured GraphQL operation profile.

