Ant exposes fetch() as a global, so you can make HTTP requests from anywhere in your script without importing anything. The API is fully WinterTC-compatible, meaning it behaves the same as in modern browsers and other standards-compliant runtimes. You can use it in top-level scripts, inside server request handlers, or anywhere async code runs.
Basic GET request
The simplest call passes a URL string and awaits the response.
const response = await fetch('https://ifconfig.co/json');
const data = await response.json();
console.log(data.ip);
The response.json() method parses the body as JSON and resolves with the result. This is the pattern used throughout the Ant examples:
const fetchJson = (url, options) => fetch(url, options).then(r => r.json());
const { ip } = await fetchJson('https://ifconfig.co/json');
console.log(ip);
POST with a JSON body
Pass an options object as the second argument to set the method, body, and headers.
const fetchJson = (url, options) => fetch(url, options).then(r => r.json());
const { json, headers } = await fetchJson('https://httpbingo.org/post', {
method: 'POST',
body: JSON.stringify({ runtime: 'ant' }),
headers: {
'Content-Type': 'application/json',
'User-Agent': 'ant/alpha (ant)'
}
});
console.log(JSON.stringify(json));
console.log(JSON.stringify(headers));
Always set Content-Type: application/json when sending a JSON body. The runtime does not add this header automatically.
Reading the response
A Response object gives you several ways to read the body depending on what the server returns.
.json()
.text()
.arrayBuffer()
.blob()
const res = await fetch('https://api.example.com/data');
const obj = await res.json();
const res = await fetch('https://api.example.com/readme');
const text = await res.text();
const res = await fetch('https://example.com/file.wasm');
const buffer = await res.arrayBuffer();
const bytes = new Uint8Array(buffer);
const res = await fetch('https://example.com/image.png');
const blob = await res.blob();
Use the headers option to pass any HTTP headers with your request.
const res = await fetch('https://api.github.com/zen', {
headers: {
'Accept': 'application/json',
'Authorization': `Bearer ${process.env.GITHUB_TOKEN}`
}
});
const text = await res.text();
console.log(text);
You can also construct a Headers object explicitly:
const headers = new Headers();
headers.set('Content-Type', 'application/json');
headers.set('X-Request-ID', crypto.randomUUID());
const res = await fetch('https://api.example.com/endpoint', {
method: 'POST',
headers,
body: JSON.stringify({ hello: 'world' })
});
Using Request and Response objects
You can construct a Request object when you want to reuse or inspect request details before sending.
const req = new Request('https://api.example.com/users', {
method: 'GET',
headers: { 'Accept': 'application/json' }
});
const res = await fetch(req);
const users = await res.json();
Response.json() is a static helper that creates a JSON response — most useful inside server handlers, but also available anywhere:
const res = Response.json({ status: 'ok', runtime: 'ant' });
console.log(res.headers.get('Content-Type')); // application/json
Checking response status
Always check response.ok or response.status before reading the body in production code.
const res = await fetch('https://api.example.com/resource');
if (!res.ok) {
throw new Error(`Request failed: ${res.status} ${res.statusText}`);
}
const data = await res.json();
true when status is in the 200–299 range.
HTTP status code (e.g. 200, 404, 500).
HTTP status message (e.g. "OK", "Not Found").
The response headers as a Headers object.
fetch inside a server handler
You can call fetch from within a server fetch handler to proxy or compose data from upstream services. Ant handles the concurrent I/O transparently.
addRoute(router, 'GET', '/echo', async () => {
const res = await fetch('http://localhost:8000/meow');
return new Response(await res.text(), {
headers: { 'X-Ant': 'meow' }
});
});
Similarly, you can fetch external APIs and return the result directly:
addRoute(router, 'GET', '/zen', async () => {
const response = await fetch('https://api.github.com/zen');
return new Response(await response.text());
});
addRoute(router, 'GET', '/api/v2/demo', async () => {
const data = await fetch('https://themackabu.dev/test.json');
return Response.json(await data.json());
});
Running multiple requests in parallel
Use Promise.all to fire multiple requests concurrently and wait for all of them.
const fetchJson = (url, options) => fetch(url, options).then(r => r.json());
await Promise.all([
fetchJson('https://ifconfig.co/json').then(({ ip }) => console.log('ip:', ip)),
fetchJson('https://themackabu.dev/test.json').then(d => console.log('test:', d)),
fetchJson('https://httpbingo.org/post', {
method: 'POST',
body: JSON.stringify({ runtime: 'ant' }),
headers: { 'Content-Type': 'application/json', 'User-Agent': 'ant/alpha (ant)' }
}).then(({ json }) => console.log('post echo:', json))
]);