Client Usage β
vRPC provides a high-fidelity type-safe interface for your API. Because it is zero-runtime, the way you initialize your client determines how the compiler optimizes your code.
ποΈ Initializing the Client β
The vRPCClient is the entry point for your API. You typically create it in a file like src/vrpc/client.ts.
Basic Setup β
import { vRPCClient, type vRPCResponse, type vRPCResponseBundle } from 'vrpc'
import fetcher from './rpc.mutator'
export const api = new vRPCClient({
baseUrl: 'https://api.example.com',
fetcher,
}).proxy as unknown as TypedClient<RpcOperations, vRPCResponse<vRPCResponseBundle>>Using Environment Variables β
In modern environments, you should never hardcode your API URL. vRPC works seamlessly with bundler-provided environment variables.
Vite / Astro β
baseUrl: import.meta.env.VITE_API_URL || '/'Next.js / Webpack β
baseUrl: process.env.NEXT_PUBLIC_API_URL || '/'π οΈ Configuration Options β
| Option | Type | Description |
|---|---|---|
baseUrl | string | The prefix for all API calls (e.g. /api/v1). |
fetcher | Fetcher | Your Custom Mutator. |
serializers | Record<string, Serializer> | Custom Body Serializers. |
meta | () => Promise<any> | (Dev only) The lazy-loaded metadata map. |
The Core Contract: ResponsePackage β
By default, vRPC operations deliver a Response Package. This structure is the "unit of work" for a single HTTP response and is the key to automatic type narrowing.
interface ResponsePackage<T> {
status: number // The key used for type narrowing
data: T // The typed body for this specific status
headers: Headers
contentType: string
}Making Requests β
Every generated method accepts an options object for parameters and configuration:
try {
const response = await api.createItem({
params: { id: '123' }, // Path params (/items/:id)
query: { force: true }, // Query params (?force=true)
body: { name: 'New Item' }, // Request body
})
// Handle successful response
if (response.status === 201) {
console.log('Created:', response.data)
}
} catch (error) {
// Handle network or API errors
console.error('API Error:', error)
}Handling Responses β
The way you interact with the result depends on how your Mutator is configured.
1. Default (Minimalist) β
In the default scaffolded setup, the fetcher returns the ResponsePackage directly. You narrow the status code immediately:
const response = await api.getUser({ params: { id: '1' } })
if (response.status === 200) {
// response.data is narrowed to 'User'
console.log(response.data.name)
}2. Custom Wrappers β
If you choose to wrap your responses (e.g., in a Result class), your interaction will reflect that structure:
const result = await api.getUser({ params: { id: '1' } })
if (result.success) {
const pkg = result.success
if (pkg.status === 200) {
console.log(pkg.data.name)
}
}Type Narrowing Power β
The magic of vRPC is that the data field is a discriminated union tied to the status. This is why we recommend keeping the status field prominentβit is the direct link to your API's models.
TIP
You can pass standard fetch options (signals, cache, etc.) via the options property in any call.