npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@volverjs/data

v2.0.4

Published

Repository pattern implementation with a tiny HttpClient based on Fetch API.

Downloads

576

Readme

volverjs

@volverjs/data

repository http-client url-builder fetch

Quality Gate Status Maintainability Rating Security Rating Depfu Depfu

maintained with ❤️ by

8 Wave

Install

# pnpm
pnpm add @volverjs/data

# yarn
yarn add @volverjs/data

# npm
npm install @volverjs/data --save

Usage

This library exports four main classes: Hash, UrlBuilder, HttpClient and RepositoryHttp.

import { Hash, HttpClient, RepositoryHttp, UrlBuilder } from '@volverjs/data'

Hash

The Hash class provides some static functions to generate hashes.

import { Hash } from '@volverjs/data'

const hash = Hash.cyrb53('hello world')

UrlBuilder

The UrlBuilder class provides a way to build URLs with template parameters and query string.

import { UrlBuilder } from '@volverjs/data'

const urlBuilder = new UrlBuilder({
    encodeValuesOnly: false
})
const url = urlBuilder.build('https://my.api.com/:endpoint', {
    endpoint: 'users',
    _limit: 10,
    _page: 1
})
// url = 'https://my.api.com/users?_limit=10&_page=1'

Instead of URLSearchParams, the query parameters are automatically encoded using qs library. Please refer to the UrlBuilder docs for more informations.

HttpClient

The HttpClient class is a wrapper around ky, a client based on fetch API . It provides a simple interface to make HTTP requests and uses UrlBuilder to build URLs.

import { HttpClient } from '@volverjs/data'

const client = new HttpClient({
    prefixUrl: 'https://my.api.com'
})
const response = await client.get({
    template: ':endpoint/:action?/:id',
    params: {
        endpoint: 'users',
        id: 1,
        _limit: 10,
        _page: 1
    }
})
// fetch('https://my.api.com/users/1?_limit=10&_page=1', { method: 'GET' })

Please refer to the HttpClient docs for more informations.

RepositoryHttp

The RepositoryHttp class is an implementation of the Repository interface for http requests using HttpClient. It was designed with the repository pattern in mind to provide a simple way to make CRUD operations on a REST API.

import { HttpClient, RepositoryHttp } from '@volverjs/data'

class User {
    id: number
    name: string
    surname: string
    constructor(data: { id: number, name: string, surname: string }) {
        this.id = data.id
        this.name = data.name
        this.email = data.email
    }

    get fullName() {
        return `${this.name} ${this.surname}`
    }
}

const client = new HttpClient({
    prefixUrl: 'https://my.api.com'
})

const repository = new RepositoryHttp<User>(client, 'users/:group?/:id?', {
    class: User
})

const getAdminUsers: User[] = async () => {
    const { responsePromise } = repository.read({
        group: 'admin'
    })
    const { data } = await responsePromise
    return data
}

Please refer to the RepositoryHttp docs for more informations.

Vue

You can use this library with Vue 3 with @volverjs/data/vue.

Plugin

The createHttpClient function returns a plugin that can be installed in a Vue app and has a property with the global httpClient instance: httpClientPlugin.globalInstance.

import { createHttpClient } from '@volverjs/data/vue'
import { createApp } from 'vue'
import App from './App.vue'

const app = createApp(App)
const httpClientPlugin = createHttpClient({
    prefixUrl: 'https://my.api.com'
})

app.use(httpClientPlugin, {
    globalName: 'vvHttp' // (optional) default: 'vvHttp'
})

With app.use(httpClientPlugin) the HttpClient instance will be available in all components as $vvHttp with Options API. Use globalName to change the name vvHttp.

Composition API

Alternatively, you can use the useHttpClient() and useRepositoryHttp() composables to get the HttpClient instance in a specific component.

useHttpClient()

If HttpClientPlugin is not created with createHttpClient() or the httpClient scope requested not exist (ex: useHttpClient('instanceNotExist')), then useHttpClient() throw the error HttpClient instance not found.

<script lang="ts">
import { createHttpClient, useHttpClient } from '@volverjs/data/vue'
</script>

<script lang="ts" setup>
import { computed, ref } from 'vue'

createHttpClient({
    prefixUrl: 'https://my.api.com'
})

const { client } = useHttpClient()
const isLoading = ref(false)
const isError = computed(() => error.value !== undefined)
const error = ref()
const data = ref<Data>()

interface User {
    id: number
    name: string
}

async function execute() {
    isLoading.value = true
    error.value = undefined
    try {
        const response = await client.get('users/1')
        data.value = await response.json<User>()
    }
    catch (e) {
        error.value = e.message
    }
    finally {
        isLoading.value = false
    }
}
</script>

<template>
    <div>
        <button @click="execute()">
            Execute
        </button>
        <div v-if="isLoading">
            Loading...
        </div>
        <div v-if="isError">
            {{ error }}
        </div>
        <div v-if="data?.[0]">
            {{ data?.[0].name }}
        </div>
    </div>
</template>

useHttpClient() also exposes request(), requestGet(), requestPost(), requestPut(), requestPatch() and requestDelete() methods. These methods are wrappers around the HttpClient methods with reactivity.

<script lang="ts" setup>
import { useHttpClient } from '@volverjs/data/vue'

interface User {
    id: number
    name: string
}
const { requestGet } = useHttpClient()
const { isLoading, isError, isSuccess, error, data, execute }
    = requestGet<User>('users/1', {
        immediate: false
    })
</script>

<template>
    <div>
        <button @click="execute()">
            Execute
        </button>
        <div v-if="isLoading">
            Loading...
        </div>
        <div v-if="isError">
            {{ error }}
        </div>
        <div v-if="isSuccess">
            {{ data.name }}
        </div>
    </div>
</template>

Each method returns an object with the following properties:

  • isLoading: a computed that indicates if the request is loading;
  • isError: a computed that indicates if the request has failed;
  • isSuccess: a computed that indicates if the request has succeeded;
  • error: a readonly ref that contains the error message;
  • response: a ref that contains the response;
  • data: a ref that contains the response data (.json() function);
  • execute(): a function that executes the request.

The request can be executed later by setting the immediate option to false (default: true).

<script lang="ts" setup>
import { useHttpClient } from '@volverjs/data/vue'

interface User {
    id: number
    name: string
}

const data = ref<Partial<User>>({ name: '' })

const { requestPost } = useHttpClient()
const { isLoading, isError, isSuccess, error, execute } = requestPost<User>(
    'users',
    computed(() => ({ immediate: false, json: data.value }))
)
</script>

<template>
    <form @submit.prevent="execute()">
        <div v-if="isLoading">
            Loading...
        </div>
        <div v-if="isError">
            {{ error }}
        </div>
        <div v-if="isSuccess">
            Success!
        </div>
        <input v-model="data.name" type="text">
        <button type="submit">
            Submit
        </button>
    </form>
</template>

The execute() function returns an object with the following properties:

  • responsePromise: a Promise that resolves with the response;
  • abort: a function that aborts the request;
  • signal: an AbortSignal that can be used to check if the request has been aborted.

useRepositoryHttp()

To create a RepositoryHttp instance, you can use the useRepositoryHttp() composable.

Parameters
  • template: string | HttpClientUrlTemplate,
  • options?: RepositoryHttpOptions
Example 1
<script lang="ts" setup>
import { useRepositoryHttp } from '@volverjs/data/vue'
import { computed, ref } from 'vue'

interface User {
    id: number
    name: string
}

const { repository } = useRepositoryHttp<User>('users/:id')
const isLoading = ref(false)
const isError = computed(() => error.value !== undefined)
const error = ref()
const item = ref()

async function execute() {
    isLoading.value = true
    try {
        const { responsePromise } = repository.read({ id: 1 })
        const response = await responsePromise
        item.value = response.item
    }
    catch (e) {
        error.value = e.message
    }
    finally {
        isLoading.value = false
    }
}
</script>

<template>
    <div>
        <button @click="execute">
            Execute
        </button>
        <div v-if="isLoading">
            Loading...
        </div>
        <div v-if="isError">
            {{ error }}
        </div>
        <div v-if="item">
            {{ item.name }}
        </div>
    </div>
</template>
Example 2 - Typing server response for responseAdapter
<script lang="ts" setup>
import { useRepositoryHttp } from '@volverjs/data/vue'
import { computed, ref } from 'vue'

interface IUser {
    id: number
    name: string
}

interface UserResponse {
    id: number
    firtname: string
    lastname: string
}

class User implements IUser {
    id: number
    name: string

    constructor(data: UserResponse) {
        this.id = data.id
        this.name = `${data.firtname} ${data.lastname}`
    }
}

const { repository } = useRepositoryHttp<IUser, UserResponse>('users/:id', {
    responseAdapter: raw => [new User(raw)] // -----> raw is type of UserResponse instead of "unknown"
})
const isLoading = ref(false)
const isError = computed(() => error.value !== undefined)
const error = ref()
const item = ref()

async function execute() {
    isLoading.value = true
    try {
        const { responsePromise } = repository.read({ id: 1 })
        const response = await responsePromise
        item.value = response.item
    }
    catch (e) {
        error.value = e.message
    }
    finally {
        isLoading.value = false
    }
}
</script>

<template>
    <div>
        <button @click="execute">
            Execute
        </button>
        <div v-if="isLoading">
            Loading...
        </div>
        <div v-if="isError">
            {{ error }}
        </div>
        <div v-if="data">
            {{ data.name }}
        </div>
    </div>
</template>

useRepositoryHttp() also exposes create(), read(), update() and remove() methods. These methods are wrappers around the RepositoryHttp methods with reactivity.

<script lang="ts" setup>
import { useRepositoryHttp } from '@volverjs/data/vue'

interface User {
    id: number
    name: string
}

const { read } = useRepositoryHttp<User>('users/:id')
const { isLoading, isError, error, item, execute } = read(
    { id: 1 },
    { immediate: false }
)
</script>

<template>
    <div>
        <button @click="execute">
            Execute
        </button>
        <div v-if="isLoading">
            Loading...
        </div>
        <div v-if="isError">
            {{ error }}
        </div>
        <div v-if="item">
            {{ item.name }}
        </div>
    </div>
</template>

Each method returns an object with the following properties:

  • isLoading: a computed that indicates if the request is loading;
  • isSuccess: a computed that indicates if the request has succeeded;
  • isError: a computed that indicates if the request has failed;
  • error: a readonly ref that contains the error message;
  • execute(): a function that executes the request.

create(), read(), update() also return:

  • data a ref that contains the response data;
  • metadata a ref that contains the response metadata;

read() also returns:

  • item a ref that contains the first item of the response data;

The request can be executed later by setting the immediate option to false (default: true).

<script lang="ts" setup>
import { useRepositoryHttp } from '@volverjs/data/vue'
import { computed } from 'vue'

interface User {
    id: number
    name: string
}

const { read, update } = useRepositoryHttp<User>('users/:id?')
const { isLoading: isReading, error: readError, item } = read({ id: 1 })
const {
    isLoading: isUpdating,
    error: updateError,
    execute
} = update(item, { id: 1 }, { immediate: false })

const isLoading = computed(() => isReading.value || isUpdating.value)
const error = computed(() => updateError.value || readError.value)
</script>

<template>
    <form @submit.prevent="execute()">
        <div v-if="isLoading">
            Loading...
        </div>
        <div v-if="error">
            {{ error }}
        </div>
        <template v-if="item">
            <input v-model="item.name" type="text">
            <button :disabled="isLoading" type="submit">
                Submit
            </button>
        </template>
    </form>
</template>

The execute() function returns an object with the following properties:

  • responsePromise: a Promise that resolves with the response;
  • abort: a function that aborts the request;
  • signal: an AbortSignal that can be used to check if the request has been aborted.

Advanced usage

HttpClientPlugin manage most of use cases (ex: micro-frontend with different httpClient, a SPA with authenticated API calls and public API calls, etc..). The HttpClientPlugin can manage a Map of httpClient instances.

createHttpClient( ) with scope parameter

With scope parameter on createHttpClient() multiple httpClient instances can be created. If the httpClient scope instance already exist an error is throwed: httpClient with scope ${scope} already exist.

Parameters:

options?: HttpClientInstanceOptions & { scope: string }

Example:
<script lang="ts" setup>
import { createHttpClient } from '@volverjs/data/vue'

createHttpClient({ scope: 'v2Api', prefixUrl: 'https://my.api.com/v2' })

const { requestGet } = useHttpClient('v2Api')

const { isLoading, isError, data } = requestGet<User>('users')
</script>

removeHttpClient( )

With this composable the httpClient instance can be removed from Map instances. The global httpClient instance cannot be removed.

Parameters:

scope: string,

Example:
<script lang="ts" setup>
import { addHttpClient, removeHttpClient } from '@volverjs/data/vue'

createHttpClient('v2Api', { prefixUrl: 'https://my.api.com/v2' })

const { requestGet } = useHttpClient('v2Api')

const { isLoading, isError, data } = requestGet<User>('users')

removeHttpClient('v2Api')
</script>

Note: The httpClient Map instances is NOT reactive, so after the removeHttpClient, the httpClient used before will NOT be destroyed.

Acknoledgements

The UrlBuilder class is inspired by urlcat.

License

MIT