Load data from external APIs or local collections using fetch directives. Data is fetched server-side at render time.
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#.
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.
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.
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.
Set a polling interval (in seconds) to auto-refresh the data on the client side:
<what>
fetch.notifications = "https://api.example.com/notifications"
fetch.notifications.poll = "30"
</what>
<div id="notifications">
<loop data="#notifications#" as="n">
<p>#n.message# — #n.created_at|date:"%b %d"#</p>
</loop>
</div>
The page re-fetches the data every 30 seconds and updates the content without a full page reload.
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.
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.
External fetch requests time out after 10 seconds by default. Configure this in wwwhat.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.