what

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.

site/products.html
<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:

site/dashboard.html
<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.

Tip: Headers are parsed as 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.

Warning: Polling creates ongoing requests to the upstream API. Use reasonable intervals and consider your API rate limits. (The old 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>
Tip: Set environment variables in your shell or a .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:

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.