Reference
HTTP client
Two layers: the directives, which cover most cases without a line of JavaScript, and
the full client in V.http. The guide on
fetching data teaches you to use it; this page lists everything.
Directive triggers
Each verb fires on the element's natural trigger:
| Element | Trigger |
|---|---|
<form> | submit |
<input>, <select>, <textarea> | change, or click on buttons |
| anything else | click |
v-trigger replaces the default:
<div v-get="/api/status" v-trigger="load">...</div>
<div v-get="/api/banner" v-trigger="visible">...</div>
<input v-get="/api/search" v-trigger="keyup" v-debounce="300">
<div v-get="/api/feed" v-trigger="visible.repeat">...</div>
<button v-get="/api/x" v-trigger="click.once">Only once</button>
Special triggers: load and ready fire on mount;
visible and revealed fire when the element gets close to the screen. The
modifiers accepted in the text are once and repeat.
v-target and v-swap
| Mode | What it does |
|---|---|
innerHTML | Replaces the content of the target. Default |
outerHTML, replace | Replaces the whole target |
textContent | Writes it as plain text |
beforebegin | Inserts before the target |
afterbegin, prepend | Inserts as the first child |
beforeend, append | Inserts as the last child |
afterend | Inserts after the target |
delete | Removes the target |
none | Leaves the DOM alone |
The inserted HTML is walked by Voodoo, so it can bring new directives with it.
Handling the response
| Attribute | What it does |
|---|---|
v-as="name" | Stores the response in the scope instead of writing to the DOM |
v-json-path="data.items" | Cuts out a piece of the response first |
v-template="#row" | Renders each item with a <template> from the page |
Inside the template you have item (the whole object), index and every
key of the object as a variable of its own.
v-search
| Attribute | Default |
|---|---|
v-param | the name of the input, or q |
v-debounce | 300 ms |
v-min-length | 0 |
v-resource
| Field | What it is |
|---|---|
data | The body of the response, or null |
loading | true while the request runs |
error | { name, message }, or null |
loaded | true after the first success |
reload() | Runs the request again |
set(value) | Replaces the data locally, useful for optimistic updates |
The syntax is name: url. With no name, the resource is called resource. It accepts
v-method, v-params, v-cache, v-retry,
v-timeout, v-json-path, v-poll and v-manual,
which does not fetch on mount and waits for reload().
Loading, notifications and callbacks
| Attribute | What it does |
|---|---|
v-loading | Selector of an element that stays hidden until the request starts |
v-loading-class | Class applied during the request. Default v-loading |
v-disable-loading | Disables the button while the request runs |
v-toast-success | Success notification |
v-toast-error | Error notification. Without it, the server's error becomes the message |
v-on-success | Expression run on success. Has $el, data and response |
v-on-error | Expression run on error. Has $el, error and message |
v-on-complete | Expression run every time, at the end |
v-redirect | Navigates to the URL after success |
v-scroll-to | Scrolls smoothly to the selector after success |
During the request the element also gets aria-busy="true".
Network
| Attribute | What it does |
|---|---|
v-params | Object that becomes the query string |
v-body | Expression that becomes the body of the request |
v-headers | Object of headers |
v-cache | Keeps the response for a while. GET only. Accepts 30s, 5m |
v-retry | Extra attempts on network failure and 5xx errors |
v-timeout | Time until it aborts |
v-offline-queue | Stores the request while the browser is offline and sends it later |
Events
<div v-get="/api/x"
@voodoo:before-request="console.log('going')"
@voodoo:success="console.log($detail.data)"
@voodoo:error="console.log($detail.message)"
@voodoo:complete="console.log('done')">
</div>
A new request from the same element cancels the previous one if it is still pending. That solves the classic search-as-you-type race, with no configuration at all.
The V.http client
const users = await V.http.get('/api/users');
const created = await V.http.post('/api/users', { name: 'Ann' });
await V.http.put('/api/users/1', { name: 'Bea' });
await V.http.patch('/api/users/1', { active: false });
await V.http.delete('/api/users/1');
await V.http.head('/api/users');
The shortcuts return the data only. For the full response, use request:
const response = await V.http.request({ url: '/api/users', method: 'GET' });
response.data;
response.status;
response.headers.get('x-total');
response.ok;
response.raw; // the original Response
Options
| Option | What it does |
|---|---|
params | Object that becomes the query string. Null and empty values are dropped |
headers | Headers of the request |
timeout | Milliseconds until it aborts. Default 30000. 0 turns it off |
retry | Extra attempts on network failure and 5xx. The wait doubles on each round |
retryDelay | Initial wait between attempts. Default 500 ms |
cache | Milliseconds of cache. GET only |
signal | AbortSignal to cancel |
credentials | Default same-origin |
responseType | auto, json, text, blob, arrayBuffer, formData |
offlineQueue | Stores the request while offline and sends it again when it comes back |
Body
Objects become JSON with the right header. FormData, Blob,
URLSearchParams, ArrayBuffer and text pass through as they are.
Errors
try {
await V.http.get('/api/x');
} catch (err) {
if (err instanceof V.HttpError) {
err.status; // 0 when there was no response
err.response?.data; // the body of the error
err.isNetworkError; // true on network failure, timeout or cancellation
err.config; // the configuration used
}
}
4xx errors are not retried. 5xx errors and network failures respect retry.
Interceptors
const off = V.http.interceptors.request.use((config) => {
config.headers = { ...config.headers, 'X-Tenant': currentTenant() };
return config;
});
V.http.interceptors.response.use((response) => response);
V.http.interceptors.error.use((error) => {
if (error.status === 401) location.assign('/login');
});
off(); // removes the request interceptor
Interceptors can be asynchronous. The request one has to return the configuration, and the response one has to return the response.
Authentication and default headers
V.http.setBaseURL('https://api.example.com');
V.http.setToken('my-jwt'); // Authorization: Bearer my-jwt
V.http.setToken('key', 'Token'); // Authorization: Token key
V.http.setToken(null); // removes it
V.http.setHeader('Accept-Language', 'en');
V.http.setHeader('Accept-Language', null); // removes it
Every request already carries X-Requested-With: XMLHttpRequest. On methods that write,
the CSRF token is read from <meta name="csrf-token"> and sent in
X-CSRF-TOKEN. The names are configurable in
V.http.defaults.csrfMeta and V.http.defaults.csrfHeader.
Upload with progress
const data = new FormData();
data.append('file', input.files[0]);
await V.http.upload('/api/files', data, {
onProgress: (percent, sent, total) => {
bar.style.width = percent + '%';
}
});
It uses XMLHttpRequest underneath, because that is the only thing that reports real upload progress.
Server-Sent Events and streaming
const source = V.http.sse('/api/events', {
message: (data) => console.log(data),
error: (e) => console.warn('connection dropped', e)
});
source.close();
await V.http.stream('/api/logs', (line) => console.log(JSON.parse(line)));
In SSE, JSON is parsed automatically when possible. stream delivers line by
line, which works well for NDJSON and long responses.
Cache
await V.http.get('/api/config', { cache: 300000 });
V.http.clearCache(); // clears everything
V.http.clearCache('/api/products'); // clears whatever contains the text
V.http.clearCache(/^GET \/api\//); // clears by pattern
Offline queue
await V.http.post('/api/orders', order, { offlineQueue: true });
await V.http.flushOfflineQueue(); // forces the resend
Requests with offlineQueue: true fired while the browser is offline are stored
in localStorage and sent again when the connection comes back. The immediate response is
synthetic, with status: 0 and statusText: 'offline-queued', handle that
case in the interface, or the user will think the operation is already finished.