# pnpm
pnpm add @volverjs/data
# yarn
yarn add @volverjs/data
# npm
npm install @volverjs/data --save
This 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
: acomputed
that indicates if the request is loading;isError
: acomputed
that indicates if the request has failed;isSuccess
: acomputed
that indicates if the request has succeeded;error
: a readonlyref
that contains the error message;response
: aref
that contains the response;data
: aref
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
: aPromise
that resolves with the response;abort
: a function that aborts the request;signal
: anAbortSignal
that 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
: acomputed
that indicates if the request is loading;isSuccess
: acomputed
that indicates if the request has succeeded;isError
: acomputed
that indicates if the request has failed;error
: a readonlyref
that contains the error message;execute()
: a function that executes the request.
create()
, read()
, update()
also return:
data
aref
that contains the response data;metadata
aref
that contains the response metadata;
read()
also returns:
item
aref
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
: aPromise
that resolves with the response;abort
: a function that aborts the request;signal
: anAbortSignal
that 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
.