Data Sources

The data field on pages, layouts, and components accepts named source definitions. At request time all sources are collected, deduplicated, and resolved in parallel into a single data object.

static

Inline data passed directly to the template:

{
  "site_config": {
    "type": "static",
    "data": { "name": "My Site", "description": "A cool website" }
  }
}
{{ data.site_config.name }}

db

SQL against the D1 database (or Turso). query returns an array of rows; queryOne returns a single object or null:

{
  "posts": {
    "type": "db",
    "query": "SELECT * FROM posts WHERE category = ? ORDER BY created_at DESC LIMIT 5",
    "params": ["query.category"]
  },
  "featured": {
    "type": "db",
    "queryOne": "SELECT * FROM posts WHERE featured = 1 LIMIT 1"
  }
}
{{#each data.posts}}{{this.title}}{{/each}}

SQL Guard: only data statements are allowed — SELECT, INSERT, UPDATE, DELETE, REPLACE, WITH, VALUES. Schema operations and stacked queries are rejected.

Param resolution

Param valueResolves to
"params.id"URL path parameter (/products/:id → id)
"query.q"URL query parameter (?q=shoes)
Any other stringPassed through as a literal

url

Fetch and parse JSON from an external HTTP endpoint:

{
  "weather": {
    "type": "url",
    "url": "https://api.weather.gov/points/40.7,-74.0",
    "method": "GET",
    "headers": { "Accept": "application/json" }
  }
}

file

Metadata plus a time-limited signed URL for an R2 object. The file content stays out of the template — you get a URL usable in <img>, <a>, <video>:

{
  "brochure": { "type": "file", "path": "marketing/brochure.pdf", "expires": 3600 }
}
<a href="{{ data.brochure.url }}">Download ({{ data.brochure.size }} bytes)</a>
FieldDescription
keyR2 object key
sizeFile size in bytes
uploadedISO timestamp
urlSigned URL at /__r2/{key}?exp=...&sig=... (HMAC-SHA256)

How resolution works

  1. Collect data configs from page + layout + referenced components.
  2. Deduplicate by content — identical SQL/params, URLs, or file paths resolve once and are shared.
  3. Resolve in parallel: unique DB queries in a single db.batch() round-trip, URL fetches concurrently, file ops via parallel R2.head() + signing.
  4. Merge results — page > layout > component on key collisions.