Overview

Mates ships thin HTTP helpers on top of fetch: Get, Post, Put, Patch, Delete, and generic Fetch. Each returns a cancellable ZeroPromise.

Every helper takes a single FetchInput — a URL string or a FetchRequest (url/path, host, params, body, headers, …). Path tokens like :id are filled from params; remaining keys become the query string.

Shared host, interceptors, and getAction factories live on FetchClient.

Helpers vs client

Approach
Best for
Notes
Get('/users') One-off Default global client — scripts and simple pages.
FetchClient / useUtils() App / module Shared host + interceptors — see FetchClient.
asyncAction / *Action UI data Reactive loading / error / data atoms around the same helpers.

Live Get()

Real GET against JSONPlaceholder. Helpers return ZeroPromise — await like a Promise.

import { html, atom, Get, renderApp } from 'mates';

const App = () => {
const title = atom('');
const busy = atom(false);
const err = atom('');

const load = async () => {
busy.set(true); err.set(''); title.set('');
try {
const todo = await Get({
url: 'https://jsonplaceholder.typicode.com/todos/1',
});
title.set(todo.title);
} catch (e) {
err.set(e.message || String(e));
} finally {
busy.set(false);
}
};

return () => html`
<div class="m-col m-gap">
<button class="btn-primary" ?disabled=${busy()} @click=${load}>
${busy() ? 'Loading…' : 'GET todo #1'}
</button>
${err() ? html`<p>${err()}</p>` : ''}
${title() ? html`<p class="muted">${title()}</p>` : ''}
</div>
`;
};
renderApp(App, document.getElementById('app'));

Path params

url: '/todos/:id' + params: { id } substitutes the path token.

import { html, atom, Get, renderApp } from 'mates';

const App = () => {
const id = atom(1);
const title = atom('');
const busy = atom(false);

const load = async () => {
busy.set(true); title.set('');
try {
const todo = await Get({
url: 'https://jsonplaceholder.typicode.com/todos/:id',
params: { id: id() },
});
title.set('#' + todo.id + ' — ' + todo.title);
} catch (e) {
title.set(e.message || String(e));
} finally {
busy.set(false);
}
};

return () => html`
<div class="m-col m-gap">
<div class="m-flex m-items-center m-justify-center m-gap-sm">
<button @click=${() => id.set(1)}>id=1</button>
<button @click=${() => id.set(2)}>id=2</button>
<button class="btn-primary" ?disabled=${busy()} @click=${load}>Fetch</button>
</div>
<p class="muted m-t-5">params.id=${id()}</p>
${title() ? html`<p class="muted">${title()}</p>` : ''}
</div>
`;
};
renderApp(App, document.getElementById('app'));