Examples
A cookbook of realistic templates. Each recipe shows a scenario, the Triplate template, and the exact rendered output. For the formal rules behind each construct see the Specification; for the host API see the API reference.
Inject a VALUES list from an array
The ${...x} spread operator expands an array of a scalar type, serialized
per its declared type and space-separated by default — ideal for VALUES.
---params { graphs: iri[] }---SELECT * WHERE { GRAPH ?g { ?s ?p ?o } VALUES ?g { ${...graphs} }}SELECT * WHERE { GRAPH ?g { ?s ?p ?o } VALUES ?g { <http://ex.org/g1> <http://ex.org/g2> }}When you need a per-row layout or tuples, use {% for %} instead:
---params { classes: iri[] }---SELECT ?s WHERE { ?s a ?type . VALUES ?type {{% for c in classes %} ${c}{% endfor %} }}SELECT ?s WHERE { ?s a ?type . VALUES ?type { <http://ex.org/Person> <http://ex.org/Org> }}Build a FILTER … IN list
A spread with join sets the separator (padded with spaces by default; add
explicit for a verbatim ",").
---params { ids: int[] }---SELECT * WHERE { ?s ex:code ?c . FILTER(?c IN (${...ids join ","}))}SELECT * WHERE { ?s ex:code ?c . FILTER(?c IN (10 , 20 , 30))}Property-path alternatives
Spread a list of predicates into an alternative path with join "|".
---params { predicates: pname[] }---SELECT ?o WHERE { ?s ${...predicates join "|"} ?o .}SELECT ?o WHERE { ?s rdfs:label | skos:prefLabel | foaf:name ?o .}Federated query with an optional limit
${endpoint} injects a SERVICE IRI; the {% if %} block disappears entirely
when the optional limit is absent (the directive’s line is trimmed).
---params { endpoint: iri, type: iri, limit: int optional }---SELECT ?s WHERE { SERVICE ${endpoint} { ?s a ${type} . }}{% if limit %}LIMIT ${limit}{% endif %}Rendered without limit:
SELECT ?s WHERE { SERVICE <http://dbpedia.org/sparql> { ?s a <http://xmlns.com/foaf/0.1/Person> . }}Mint IRIs from identifiers
$<…> percent-encodes each hole as one opaque path component, so untrusted ids
can never break out of the IRI (note the encoded space in bob 2).
---params { ids: string[] }---SELECT ?p WHERE { VALUES ?p {{% for id in ids %} $<http://example.org/person/${id}>{% endfor %} }}SELECT ?p WHERE { VALUES ?p { <http://example.org/person/alice> <http://example.org/person/bob%202> }}Language-tagged and typed literals
$"…" builds an escaped string literal; add a @${lang} tag (static or dynamic)
or a ^^ datatype. A date-typed value serializes to a canonical typed literal.
---params { label: string, lang: string, born: date }---INSERT DATA { ex:x rdfs:label $"${label}"@${lang} ; ex:born ${born} .}INSERT DATA { ex:x rdfs:label "Köln"@de ; ex:born "1989-11-09"^^<http://www.w3.org/2001/XMLSchema#date> .}Generate RDF data, not just queries
Triplate is host-agnostic — the same constructs emit Turtle/TriG. Loop over records to build a dataset.
---params { people: { id: iri, name: string, age: int }[] }---@prefix ex: <http://example.org/> .{% for p in people %}${p.id} a ex:Person ; ex:name $"${p.name}" ; ex:age ${p.age} .{% endfor %}@prefix ex: <http://example.org/> .<http://example.org/alice> a ex:Person ; ex:name "Alice" ; ex:age 30 .<http://example.org/bob> a ex:Person ; ex:name "Bob" ; ex:age 25 .Custom types
Need a domain type? Register a serializer once and use it as a header type. See Extensibility for each language’s signature.
import { registerType } from 'triplate';
registerType('uuidref', (value, pos) => { if (!/^[0-9a-f-]{36}$/.test(String(value))) { throw new Error('not a UUID'); } return `<urn:uuid:${value}>`;});// then: params { id: uuidref }Example blocks
example dbpedia "DBpedia — people & orgs" { service: <http://dbpedia.org/sparql> classes: [ foaf:Person, foaf:Organization ] limit: 10}An example <id> ["<description>"] { … } block (in the --- frontmatter,
alongside params) declares a named, validated set of sample values. They are
development/preview fixtures, not production defaults: render(context)
still requires real values, while previewExample(id) / a CLI renders with a
set so a query is runnable while you develop. An IDE lists blocks by id and
shows the description. Prefixed names resolve against the template’s PREFIX
declarations.
Inert regions
Comments (#), complete <…> IRIs, and string literals ("…", '…',
triple-quoted) pass through verbatim — a $ or { inside them is literal text.
A # inside a string or IRI is not a comment. Frontmatter comments are never
emitted, but the parser retains them as positioned symbols so tools like
formatters can preserve and re-indent them; a comment in the body is emitted.
Detection
A leading --- frontmatter header is a positive “this is a Triplate template”
marker, complementing the fail-fast guarantee (the body is invalid host syntax
until rendered).