Fetch & APIs
Load data from external APIs or local collections using fetch directives. Data is fetched server-side at render time.
Fetch Directive
Declare a fetch in the <what> block. The key becomes a template variable containing the response data.
<what>
fetch.products = "https://api.example.com/products"
</what>
<loop data="#products#" as="product">
<div>#product.name# — #product.price|currency#</div>
</loop>
The URL is fetched on each request (subject to caching). The response is parsed as JSON and made available as #products#.
Using Response Data
Access the response directly or navigate into it with dot notation:
<what>
fetch.data = "https://api.example.com/items"
</what>
<!-- If response is an array -->
<p>First item: #data.0.name#</p>
<!-- If response is an object -->
<p>Total: #data.total#</p>
<p>Status: #data.meta.status#</p>
When the response is an array, use it directly in <loop>. When it's an object, reference nested properties with dot notation.
HTTP Methods
By default, fetch uses GET. Set the method, headers, and body as sub-properties of the fetch key:
<what>
fetch.orders = "https://api.example.com/orders"
fetch.orders.method = "POST"
fetch.orders.headers = "Authorization: Bearer #env.API_TOKEN#"
fetch.orders.body = '{"status": "active", "limit": 50}'
</what>
<loop data="#orders#" as="order">
<div>#order.id# — #order.total|currency#</div>
</loop>
Supported methods: GET, POST, PUT, PATCH, DELETE.
Key: Value pairs. For multiple headers, separate them with newlines or semicolons.
JSON Path Extraction
When an API wraps its data in an envelope, use the path sub-property to extract the nested data:
<what>
fetch.users = "https://api.example.com/v2/users"
fetch.users.path = "data.results"
</what>
<!-- #users# now contains the array at response.data.results -->
<loop data="#users#" as="user">
<p>#user.name#</p>
</loop>
The path uses dot notation to navigate into the response JSON. For example, if the API returns:
{
"status": "ok",
"data": {
"results": [
{"name": "Alice"},
{"name": "Bob"}
]
}
}
Then fetch.users.path = "data.results" extracts the array directly.
Polling
To auto-refresh fetched data, move the markup into a partial and wrap it in <what-fetch>. The partial re-runs its own fetch.* directives on every request, so each poll gets fresh data:
<!-- partials/notifications.html -->
<what>
fetch.notifications = "https://api.example.com/notifications"
</what>
<loop data="#notifications#" as="n">
<p>#n.message# — #n.created_at|date:"%b %d"#</p>
</loop>
<!-- the page -->
<what-fetch url="/w-partial/notifications" poll="30s">
<include src="partials/notifications"/>
</what-fetch>
The region refreshes every 30 seconds without a full page reload; the <include> renders the first state on the server so the page never appears empty.
fetch.<key>.poll directive was removed in v1.3 — it was never functional.)
Environment Variables
Reference environment variables with #env.*# syntax in fetch URLs and headers. Variables are resolved server-side and never exposed to the client:
<what>
fetch.weather = "https://api.weather.com/v1/current?key=#env.WEATHER_KEY#&city=London"
</what>
<p>Temperature: #weather.temp#°C</p>
<what>
fetch.data = "https://api.example.com/secure"
fetch.data.headers = "Authorization: Bearer #env.API_TOKEN#"
fetch.data.headers = "X-API-Version: 2"
</what>
.env file before starting the dev server. They're available as #env.VARIABLE_NAME# anywhere in templates.
Local Data
Use the local: prefix to query your project's data store instead of an external API:
<what>
fetch.posts = "local:posts"
</what>
<loop data="#posts#" as="post">
<h2>#post.title#</h2>
<p>#post.excerpt#</p>
</loop>
Local fetch supports sort, filter, search, limit, and offset sub-properties:
<what>
fetch.recent = "local:posts"
fetch.recent.sort = "created_at:desc"
fetch.recent.filter = "status=published"
fetch.recent.limit = "10"
</what>
See Data Sources for the full local query reference including search, filter operators, and pagination.
Timeout
External fetch requests time out after 10 seconds by default. Configure this in what.toml:
[server]
fetch_timeout = 15 # seconds
If a fetch times out, the variable is set to an empty value. Use the default filter or a conditional to handle this gracefully.