# pnpm
pnpm add @volverjs/data
# yarn
yarn add @volverjs/data
# npm
npm install @volverjs/data --saveThis library exports four main classes: Hash, UrlBuilder, HttpClient and RepositoryHttp.
import { Hash, HttpClient, RepositoryHttp, UrlBuilder } from '@volverjs/data'The Hash class provides some static functions to generate hashes.
import { Hash } from '@volverjs/data'
const hash = Hash.cyrb53('hello world')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.
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.
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.
You can use this library with Vue 3 with @volverjs/data/vue.
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.
Alternatively, you can use the useHttpClient() and useRepositoryHttp() composables to get the HttpClient instance in a specific component.
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: acomputedthat indicates if the request is loading;isError: acomputedthat indicates if the request has failed;isSuccess: acomputedthat indicates if the request has succeeded;error: a readonlyrefthat contains the error message;response: arefthat contains the response;data: arefthat 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: aPromisethat resolves with the response;abort: a function that aborts the request;signal: anAbortSignalthat can be used to check if the request has been aborted.
To create a RepositoryHttp instance, you can use the useRepositoryHttp() composable.
template:string | HttpClientUrlTemplate,options?:RepositoryHttpOptions
<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><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: acomputedthat indicates if the request is loading;isSuccess: acomputedthat indicates if the request has succeeded;isError: acomputedthat indicates if the request has failed;error: a readonlyrefthat contains the error message;execute(): a function that executes the request.
create(), read(), update() also return:
dataarefthat contains the response data;metadataarefthat contains the response metadata;
read() also returns:
itemarefthat 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: aPromisethat resolves with the response;abort: a function that aborts the request;signal: anAbortSignalthat can be used to check if the request has been aborted.
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.
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.
options?: HttpClientInstanceOptions & { scope: string }
<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>With this composable the httpClient instance can be removed from Map instances.
The global httpClient instance cannot be removed.
scope: string,
<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.
The UrlBuilder class is inspired by urlcat.