5.14 sparql — a SPARQL endpoint as a keyed source
A sparql producer joins any endpoint speaking the SPARQL 1.1 Protocol — a public knowledge
graph (Wikidata, UniProt, EU Cellar) or an organization’s own triplestore. The realm authors the
SELECT query ONCE; consumers only ever see the virtual join, and no query author writes SPARQL.
- name: orgByRegistryId kind: sparql endpoint: "https://query.example.org/sparql" query: | SELECT ?registryId ?org ?orgLabel ?parentLabel WHERE { VALUES ?registryId { {{keys}} } ?org <http://example.org/prop/registryId> ?registryId . OPTIONAL { ?org <http://example.org/prop/parent> ?parent . } } keyVar: registryId keyForm: literal # literal (default) | iri | number fetch: maxKeysPerCall: 100 # keys per request; larger batches are split timeoutSeconds: 60 maxRows: 10000 userAgent: "MyRealm/1.0 (contact url)" # public endpoints often require one bearerTokenEnv: MY_TRIPLESTORE_TOKEN # env var; never a credential in YAML cache: { kind: ttl, seconds: 86400 }The contract:
- One batch, one query. The whole key batch is serialized into the query at the
{{keys}}token — the natural spot is aVALUESclause — so a key set is one request (chunked only byfetch.maxKeysPerCall). A query without the token is rejected at fetch time with a loud error, never an empty result. - Keys are serialized safely, never spliced raw.
keyForm: literalemits escaped quoted strings;iriemits<key>and rejects keys that cannot be an IRI;numberadmits only numerics. A key the form cannot represent is SKIPPED and reported inwarnings— it can never inject query text, and it can never silently read as “no such record”. - Rows are the SELECT bindings. Each solution becomes one record, result variable → value.
Common XSD numerics and booleans arrive as numbers/booleans; IRIs arrive as their full string;
a variable absent from a solution (an unmatched
OPTIONAL) is absent from the record. keyVaris the identity echo. The declared variable must be selected and must carry the anchor key back on every row — that is what links a row to its anchor. AkeyVarthe query does not select is reported as a warning (the join would otherwise silently never form).- Honesty. A failed fetch (network, HTTP error, unparseable response) is a
warningsentry plus an empty result — never a silent 0. Exceedingfetch.maxRowstruncates and reports a TRUNCATED warning. A retryable refusal — a throttle, a gateway failure, a timeout — is attempted once more, honouring the endpoint’sRetry-Afterwhere it sends one: the same law every producer kind gets (§9, what a flaky source costs), not asparqlspeciality. - Credentials stay out of YAML. An authenticated endpoint names an environment variable
(
fetch.bearerTokenEnv); public endpoints need nothing.
Cost every consumer should know: each key batch is one live query against an endpoint the realm does not control. Public endpoints enforce their own timeouts and rate limits; a realm should bound its batches, declare a TTL cache, and expect the occasional refused query at peak — which surfaces as a reported fetch failure, not as missing data.

