Kweri supports two usage patterns. Choose the one that fits your project:
| Path A — With an OpenAPI spec | Path B — Without an OpenAPI spec |
|---|
| Endpoints | Generated by kweri-gen | Defined manually with defineEndpoint |
| Hooks | useGet, usePost, usePut, usePatch, useDelete | useQuery, useMutation |
| Best for | Teams with a Swagger / OpenAPI spec | Teams hand-crafting their API layer |
Path A — With an OpenAPI spec (recommended)
1. Install
npm install kweri
# or
bun add kweri
2. Create a Kweri instance
// src/lib/kweri.ts
import { Kweri } from 'kweri'
export const kweri = new Kweri({
baseURL: 'https://api.example.com',
staleTime: 30_000,
cacheTime: 300_000,
enableDevTools: true
})
3. Generate from your spec
npx kweri-gen https://api.example.com/openapi.json
This writes .generated/client.js with TypeBox schemas and an EndpointByMethod map for every route in your spec. Re-run whenever your API changes.
4. Set up path hooks
// src/hooks/useKweri.ts
import { useSyncExternalStore } from 'react'
import { createReactPathHooks } from 'kweri'
import { EndpointByMethod } from 'kweri/generated'
import { kweri } from '@/lib/kweri'
export const { useGet, usePost, usePut, usePatch, useDelete } =
createReactPathHooks(useSyncExternalStore, kweri, EndpointByMethod)
export { kweri }
// src/composables/useKweri.ts
import { ref, watch, onUnmounted } from 'vue'
import { createVuePathHooks } from 'kweri'
import { EndpointByMethod } from 'kweri/generated'
import { kweri } from '@/lib/kweri'
export const { useGet, usePost, usePut, usePatch, useDelete } =
createVuePathHooks({ ref, watch, onUnmounted }, kweri, EndpointByMethod)
export { kweri }
Create the hook factory once and export it. Don’t call createReactPathHooks or createVuePathHooks inside a component.
5. Fetch data in your components
// src/components/UserList.tsx
import { useGet, usePost, kweri } from '@/hooks/useKweri'
export function UserList() {
const { data, isLoading, isError, error } = useGet('/users', {})
if (isLoading) return <p>Loading...</p>
if (isError) return <p>Error: {error?.message}</p>
return (
<ul>
{data?.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
)
}
export function CreateUserButton() {
const { mutateAsync, isLoading } = usePost('/users')
async function handleClick() {
await mutateAsync({ body: { name: 'Alice' } })
kweri.invalidateByPath('/users')
}
return (
<button onClick={handleClick} disabled={isLoading}>
{isLoading ? 'Creating...' : 'Create user'}
</button>
)
}
<!-- src/components/UserList.vue -->
<script setup lang="ts">
import { useGet, usePost, kweri } from '@/composables/useKweri'
const { data, isLoading, isError, error } = useGet('/users')
const createMutation = usePost('/users')
async function handleCreate() {
await createMutation.mutateAsync({ body: { name: 'Alice' } })
kweri.invalidateByPath('/users')
}
</script>
<template>
<p v-if="isLoading.value">Loading...</p>
<p v-else-if="isError.value">Error: {{ error.value?.message }}</p>
<ul v-else>
<li v-for="user in data.value" :key="user.id">{{ user.name }}</li>
</ul>
<button @click="handleCreate" :disabled="createMutation.isLoading.value">
Create user
</button>
</template>
Path B — Without an OpenAPI spec
Use this path when you don’t have a spec and want to define endpoints by hand.
1. Install
npm install kweri
# or
bun add kweri
2. Create a Kweri instance
// src/lib/kweri.ts
import { Kweri } from 'kweri'
export const kweri = new Kweri({
baseURL: 'https://api.example.com',
staleTime: 30_000,
cacheTime: 300_000,
})
3. Define your endpoints
// src/api/users.ts
import { Type, defineEndpoint } from 'kweri'
export const getUsers = defineEndpoint({
method: 'GET',
path: '/users',
params: Type.Object({}),
response: Type.Array(
Type.Object({
id: Type.Number(),
name: Type.String(),
email: Type.String()
})
)
})
export const createUser = defineEndpoint({
method: 'POST',
path: '/users',
params: Type.Object({
body: Type.Object({ name: Type.String(), email: Type.String() })
}),
response: Type.Object({ id: Type.Number(), name: Type.String(), email: Type.String() })
})
4. Set up hooks
// src/hooks/useKweri.ts
import { useSyncExternalStore } from 'react'
import { createReactQueryHooks } from 'kweri'
import { kweri } from '@/lib/kweri'
export const { useQuery, useMutation } =
createReactQueryHooks(useSyncExternalStore, kweri)
// src/composables/useKweri.ts
import { ref, watch, onUnmounted } from 'vue'
import { createVueQueryHooks } from 'kweri'
import { kweri } from '@/lib/kweri'
export const { useQuery, useMutation } =
createVueQueryHooks({ ref, watch, onUnmounted }, kweri)
export { kweri }
5. Fetch data in your components
// src/components/UserList.tsx
import { useQuery, useMutation, kweri } from '@/hooks/useKweri'
import { getUsers, createUser } from '@/api/users'
export function UserList() {
const { data, isLoading, isError, error } = useQuery(getUsers, {})
if (isLoading) return <p>Loading...</p>
if (isError) return <p>Error: {error?.message}</p>
return (
<ul>
{data?.map(user => <li key={user.id}>{user.name}</li>)}
</ul>
)
}
export function CreateUserButton() {
const { mutateAsync, isLoading } = useMutation(createUser)
async function handleClick() {
await mutateAsync({ body: { name: 'Alice', email: '[email protected]' } })
kweri.invalidateByPath('/users')
}
return (
<button onClick={handleClick} disabled={isLoading}>
{isLoading ? 'Creating...' : 'Create user'}
</button>
)
}
<!-- src/components/UserList.vue -->
<script setup lang="ts">
import { useQuery, useMutation, kweri } from '@/composables/useKweri'
import { getUsers, createUser } from '@/api/users'
const { data, isLoading, isError, error } = useQuery(getUsers, {})
const createMutation = useMutation(createUser)
async function handleCreate() {
await createMutation.mutateAsync({ body: { name: 'Alice', email: '[email protected]' } })
kweri.invalidateByPath('/users')
}
</script>
<template>
<p v-if="isLoading.value">Loading...</p>
<p v-else-if="isError.value">Error: {{ error.value?.message }}</p>
<ul v-else>
<li v-for="user in data.value" :key="user.id">{{ user.name }}</li>
</ul>
<button @click="handleCreate" :disabled="createMutation.isLoading.value">
Create user
</button>
</template>
import { kweri } from './lib/kweri'
import { getUsers } from './api/users'
const unsubscribe = kweri.subscribe(getUsers, {}, (entry) => {
console.log('Cache updated:', entry.data)
})
await kweri.query(getUsers, {})
unsubscribe()