viacomm
v1.1.8
Published
Thin layer over the fetch API, with built-in support for Vue 3.
Readme
ViaComm
Simple Fetch Wrapper with Vue 3 Plugin Support
ViaComm is a small wrapper around the browser's fetch API. It provides:
- Shared defaults for all requests
- Automatic URL and query-string handling
- Separate request data handling for
GET/HEADand other methods - Convenient JSON requests
- Dynamic request and header values
- Consistent errors for unsuccessful HTTP responses
- Optional Vue 3 plugin support through the
$commglobal property
Installation
Install ViaComm from npm:
npm install viacommViaComm is intended for browser environments and requires the Fetch API, URL, URLSearchParams, Headers, and structuredClone.
Basic Usage
The package exports a configured comm singleton as its default export:
import comm from "viacomm";
const response = await comm.request({
url: "/api/users",
});
const users = await response.json();You can also import the named singleton:
import {comm} from "viacomm";Every request returns a Promise<Response>, so the response can be handled using the standard Fetch API methods such as json(), text(), blob(), or arrayBuffer().
The Comm Class
The Comm class is the main request client. A new instance can be created when an application needs an independent set of defaults:
import {Comm} from "viacomm";
const api = new Comm();
const response = await api.request({
url: "/api/status",
});The package's default singleton is suitable for most applications.
Default Request Options
A new Comm instance starts with these defaults:
{
cache: "default",
credentials: "same-origin",
keepalive: false,
method: "GET",
redirect: "follow",
referrer: "client",
referrerPolicy: "no-referrer-when-downgrade"
}These values are passed to fetch unless overridden for an individual request.
Configuring Defaults
Use configure() to add or replace defaults for every request:
import comm from "viacomm";
comm.configure({
headers: {
"X-Client": "my-app"
},
credentials: "include"
});Configuration is merged into the existing defaults. Calling configure() with a falsey value resets the defaults to their original values:
comm.configure(false);Request-specific options take precedence over configured defaults.
Making Requests
request(options)
request() is an asynchronous method that returns a Promise<Response>. It accepts Fetch options plus the following ViaComm-specific options:
| Option | Type | Description |
| --- | --- | --- |
| url | string or URL | Required request URL. Relative URLs use window.location.origin. |
| data | object or string | Query data for GET/HEAD, or request body data for other methods. |
| headers | object or Function | Headers for this request. |
| method | string | HTTP method, such as GET, POST, PUT, or DELETE. |
| body | any | Standard Fetch request body. data takes precedence for non-GET/HEAD requests. |
Always await the returned promise or handle it with .then():
const response = await comm.request({
url: "/api/products"
});A relative URL:
const response = await comm.request({
url: "/api/products"
});To resolve a relative URL against an API host, provide baseURL:
const response = await comm.request({
baseURL: "https://api.example.com/v1/",
url: "/products"
});The resulting request is sent to https://api.example.com/products. baseURL only affects relative URLs; an absolute url remains unchanged:
const response = await comm.request({
baseURL: "https://api.example.com/v1/",
url: "https://uploads.example.com/image.png"
});To use the full baseURL with a relative URL, don't lead with a slash:
const response = await comm.request({
baseURL: "https://api.example.com/v1/",
url: "products"
});The resulting request is sent to https://api.example.com/v1/products.
An absolute URL:
const response = await comm.request({
url: "https://example.com/api/products"
});Query Parameters
For GET and HEAD requests, data is added to the URL as query parameters. Existing query parameters are preserved, and matching values in data override them.
Values that are null or undefined are ignored:
const response = await comm.request({
url: "/api/products?sort=name",
data: {
page: 2,
limit: 25,
discontinued: null
}
});This produces a request similar to:
/api/products?sort=name&page=2&limit=25Request Bodies
For methods other than GET and HEAD, data is assigned to the Fetch request body:
const response = await comm.request({
url: "/api/products",
method: "POST",
data: "product data"
});For JSON payloads, use json() instead.
HTTP Verb Shortcuts
delete(), get(), head(), patch(), post(), put(), and options() are asynchronous convenience methods. Each returns a Promise<Response>, so calls should be awaited or handled as promises.
GET and HEAD shortcuts use query parameters and do not use JSON handling by default.
All other shortcuts send data as JSON by default.
const response = await comm.post("/api/products", {
data: {
name: "Example product"
}
});To send a non-JSON request, pass false as the second argument:
const response = await comm.post({
url: "/api/products",
data: "product data"
}, false);JSON Requests
json(options)
json() is an asychronous convenience method that returns a Promise<Response> for requests that send JSON. It:
- Sets the
Acceptheader toapplication/json, text/plain, */* - Sets the
Content-Typeheader toapplication/json - Serializes non-string
datawithJSON.stringify() - Delegates to
request()
const response = await comm.json({
url: "/api/products",
method: "POST",
data: {
name: "Example product",
price: 19.99
}
});
const product = await response.json();You can still provide additional headers:
await comm.json({
url: "/api/products",
method: "POST",
headers: {
Authorization: "Bearer token"
},
data: {
name: "Example product"
}
});The JSON headers supplied by json() take precedence over matching headers supplied in the request options.
Dynamic Options and Headers
Option values may be functions. ViaComm evaluates them immediately before sending the request, passing the current request options:
comm.configure({
headers: {
Authorization: (url, data, options) =>
`Bearer ${getAccessToken()}`
}
});Headers may also be functions that receive the resolved URL, request data, and request options:
await comm.request({
url: "/api/account",
headers: (url, data, options) => ({
"X-Request-Method": options.method
})
});Individual header values can be functions as well:
await comm.request({
url: "/api/account",
headers: {
"X-Request-ID": () => crypto.randomUUID()
}
});Request-specific headers are merged with configured headers rather than replacing them entirely.
Error Handling
When fetch() receives an unsuccessful HTTP response, ViaComm rejects the promise with a CommError. The original Response is available through the error's response property:
import {CommError} from "viacomm";
try {
const response = await comm.request({
url: "/api/products/does-not-exist"
});
} catch (error) {
if (error instanceof CommError) {
console.error(error.message);
console.error(error.response.status);
} else {
// Network and other non-HTTP errors are handled normally.
console.error(error);
}
}A missing url causes the returned promise to reject:
try {
await comm.request({});
} catch (error) {
// Error: You must define a 'url' to make the request against.
}The same behavior applies to json() and all HTTP verb shortcuts.
Request Hooks
Hooks allow code to run before a request is sent or after a response is received. Hooks
may be registered on an individual Comm instance or on the shared comm singleton.
Before Hooks
Use before() to register a function that runs immediately before fetch():
const hookId = comm.before(async (url, data, options) => {
options.headers.set("X-Request-Started", new Date().toISOString());
});Before hooks receive:
- The resolved request URL as a
URLinstance - The request
data - The resolved request options object
Hooks are awaited before the request is sent, so asynchronous work can be performed
before fetch() runs.
After Hooks
Use after() to register a function that runs after fetch() resolves:
const hookId = comm.after(async (url, data, options, response) => {
// Because we receive a clone of the response,
// we can safely drain it and read its body without affecting the original.
const requestLog = await response.json();
console.log(`${options.method} ${url} returned ${response.status}`);
console.log("Request log:", requestLog);
return response;
});After hooks may be synchronous or asynchronous. If an after hook returns a promise, ViaComm waits for that promise to resolve before continuing the request pipeline.
After hooks receive the same URL, data, and options arguments, followed by a clone of the response. The cloned response allows the hook to inspect or modify its copy without changing the response used by the request pipeline.
An after hook should return the response, or a promise that resolves to the response,
that should continue through the request pipeline. If the response is unsuccessful,
the returned promise rejects with a CommError after the hook has run.
Using hook() Directly
hook() can be used when the hook phase should be selected dynamically. Hooks run
before requests by default; pass false as the second argument to register an after hook:
const beforeId = comm.hook((url, data, options) => {
console.log("Sending request:", url.toString());
});
const afterId = comm.hook(async (url, data, options, response) => {
await auditResponse(response);
console.log("Received response:", response.status);
return response;
}, false);Each registration method returns a unique hook ID:
hook(func)andhook(func, pre)return the registered hook ID.before(func)returns the registered before-hook ID.after(func)returns the registered after-hook ID.
Removing Hooks
Pass a hook ID to unhook() to remove that hook:
const hookId = comm.before(() => {
console.log("This runs before requests");
});
comm.unhook(hookId);Pass false to remove all registered hooks from the client:
comm.unhook(false);Hooks are registered independently for each Comm instance. Removing hooks from a
newly created client does not affect hooks registered on the shared singleton, and
vice versa.
Using ViaComm with Vue 3
ViaComm can be installed as a Vue 3 plugin. Installation exposes the shared singleton as $comm on every component.
Plugin Installation
Create a Vue plugin wrapper, for example plugins/comm.js:
import {comm, install} from "viacomm";
// Configure the shared singleton here if needed.
comm.configure({
headers: {
"X-Client": "my-vue-app"
}
});
export default install;Register the plugin in the application's entry point:
import {createApp} from "vue";
import App from "./App.vue";
import comm from "./plugins/comm";
const app = createApp(App);
app.use(comm);
app.mount("#app");You can also install ViaComm directly:
import {createApp} from "vue";
import {install as comm} from "viacomm";
import App from "./App.vue";
const app = createApp(App);
app.use(comm);
app.mount("#app");The plugin is safe to install more than once; repeated installation attempts are ignored.
Options API
After installation, use this.$comm inside Options API components:
<template>
<button @click="loadUsers">Load users</button>
</template>
<script>
export default {
methods: {
async loadUsers() {
const response = await this.$comm.request({
url: "/api/users"
});
this.users = await response.json();
}
},
data() {
return {
users: []
};
}
};
</script>Composition API
With the Composition API, import the singleton directly:
<script setup>
import {ref} from "vue";
import {comm} from "viacomm";
const users = ref([]);
async function loadUsers() {
const response = await comm.request({
url: "/api/users"
});
users.value = await response.json();
}
</script>
<template>
<button @click="loadUsers">Load users</button>
<pre>{{ users }}</pre>
</template>Import Options
The standard package import is:
import comm, {Comm, CommError, install} from "viacomm";The standard build expects lodash-es to be available as a peer dependency. A full build is also available, which includes the required lodash functionality:
import comm from "viacomm/dist/viacomm.full.js";Minified builds are available at:
viacomm/dist/viacomm.min.js
viacomm/dist/viacomm.full.min.jsVue is an optional peer dependency, so ViaComm can be used without Vue. The Vue plugin is only needed when installing ViaComm with app.use().
Browser Support
ViaComm uses browser APIs provided by the Fetch API, including:
fetchURLURLSearchParamsHeaderswindow.location.origin
Use an appropriate polyfill or compatible environment when targeting browsers that do not provide these APIs.
License
The LICENSE.txt file contains the license details. ViaComm is licensed under the Mozilla Public License, Version 2.0.
