https://github.com/melvishniz/vue-api-kit
A powerful and type-safe API client for Vue 3 applications with built-in validation using Zod.
https://github.com/melvishniz/vue-api-kit
api api-client axios http rest sdk typescript vue vue3 zod
Last synced: about 1 month ago
JSON representation
A powerful and type-safe API client for Vue 3 applications with built-in validation using Zod.
- Host: GitHub
- URL: https://github.com/melvishniz/vue-api-kit
- Owner: MelvishNiz
- License: mit
- Created: 2025-12-15T03:35:56.000Z (4 months ago)
- Default Branch: main
- Last Pushed: 2026-02-13T10:18:07.000Z (about 2 months ago)
- Last Synced: 2026-02-13T22:46:45.912Z (about 2 months ago)
- Topics: api, api-client, axios, http, rest, sdk, typescript, vue, vue3, zod
- Language: TypeScript
- Homepage: https://www.npmjs.com/package/vue-api-kit
- Size: 1.2 MB
- Stars: 3
- Watchers: 0
- Forks: 0
- Open Issues: 9
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# 🚀 vue-api-kit
[](https://www.npmjs.com/package/vue-api-kit)
[](https://packagephobia.now.sh/result?p=vue-api-kit)
[](https://bundlephobia.com/result?p=vue-api-kit)
[](https://npm-stat.com/charts.html?package=vue-api-kit)
[](https://github.com/MelvishNiz/vue-api-kit/actions)
[](https://github.com/MelvishNiz/vue-api-kit/blob/main/LICENSE)
A powerful and type-safe API client for Vue 3 applications with built-in validation using Zod.
## 📋 Table of Contents
- [Installation](#-installation)
- [Quick Start](#-quick-start)
- [Core Features](#-core-features)
- [Basic Usage](#-basic-usage)
- [Queries (GET)](#queries-get)
- [Queries (POST)](#queries-post)
- [Mutations (POST/PUT/DELETE)](#mutations-postputdelete)
- [Configuration](#-configuration)
- [Advanced Features](#-advanced-features)
- [Nested Structure](#nested-structure)
- [Modular API Definitions](#modular-api-definitions)
- [Request Interceptors](#request-interceptors)
- [File Upload](#file-upload)
- [CSRF Protection](#csrf-protection)
- [License](#-license)
## 📦 Installation
```bash
npm install vue-api-kit
```
## ⚡ Quick Start
```typescript
import { createApiClient } from 'vue-api-kit';
import { z } from 'zod';
// Define your API client
const api = createApiClient({
baseURL: 'https://api.example.com',
queries: {
getUsers: {
path: '/users',
response: z.array(z.object({
id: z.number(),
name: z.string(),
email: z.string()
}))
},
getUser: {
path: '/users/{id}',
params: z.object({ id: z.number() }),
response: z.object({
id: z.number(),
name: z.string(),
email: z.string()
})
}
},
mutations: {
createUser: {
method: 'POST',
path: '/users',
data: z.object({
name: z.string(),
email: z.string().email()
}),
response: z.object({
id: z.number(),
name: z.string(),
email: z.string()
})
}
}
});
```
Use in your Vue components:
```vue
import { api } from './api';
// Query - auto-loads on mount
const { result, isLoading, errorMessage } = api.query.getUsers();
// Mutation
const { mutate, isLoading: creating } = api.mutation.createUser();
async function handleCreate() {
await mutate({ name: 'John', email: 'john@example.com' });
}
Loading...
Error: {{ errorMessage }}
- {{ user.name }}
```
## 🎯 Core Features
- ✅ **Type-Safe** - Full TypeScript support with automatic type inference
- ✅ **Zod Validation** - Built-in request/response validation
- ✅ **Vue 3 Composition API** - Reactive state management
- ✅ **Lightweight** - ~7kB minified (2.2kB gzipped)
- ✅ **Auto Loading States** - Built-in loading, error, and success states
- ✅ **Path Parameters** - Automatic path parameter replacement (`/users/{id}`)
- ✅ **Debouncing** - Built-in request debouncing
- ✅ **POST Queries** - Support both GET and POST for data fetching
- ✅ **File Upload** - Multipart/form-data with nested objects
- ✅ **CSRF Protection** - Automatic token refresh (Laravel Sanctum compatible)
- ✅ **Modular** - Split API definitions across files
- ✅ **Nested Structure** - Organize endpoints hierarchically
- ✅ **Tree-Shakeable** - Only bundles what you use
## 📖 Basic Usage
### Queries (GET)
Use queries to fetch data. They automatically load on component mount:
```vue
import { api } from './api';
import { ref } from 'vue';
// Simple query - automatically loads data on mount
const { result, isLoading, errorMessage } = api.query.getUsers();
// Query with parameters - reactive to parameter changes
const userId = ref(1);
const { result: user, refetch } = api.query.getUser({
params: { id: userId }
});
// Query with options - customize behavior
const { result: data } = api.query.getUsers({
loadOnMount: true,
debounce: 300,
onResult: (data) => console.log('Loaded:', data),
onError: (error) => console.error('Error:', error)
});
- {{ user.name }}
```
### Queries (POST)
POST queries are perfect for complex searches with filters:
```typescript
// API definition
queries: {
searchUsers: {
method: 'POST',
path: '/users/search',
data: z.object({
query: z.string(),
filters: z.object({
active: z.boolean().optional(),
role: z.string().optional()
}).optional()
}),
response: z.array(z.object({ id: z.number(), name: z.string() }))
}
}
```
```vue
const searchTerm = ref('');
const { result, isLoading, refetch } = api.query.searchUsers({
data: {
query: searchTerm.value,
filters: { active: true }
},
loadOnMount: false
});
Search
```
### Mutations (POST/PUT/DELETE)
```vue
const { mutate, isLoading, result, errorMessage } = api.mutation.createUser({
onResult: (data) => console.log('Created:', data),
onError: (error) => console.error('Error:', error)
});
const name = ref('');
const email = ref('');
async function handleSubmit() {
await mutate({ name: name.value, email: email.value });
}
{{ isLoading ? 'Creating...' : 'Create User' }}
{{ errorMessage }}
```
## ⚙️ Configuration
```typescript
const api = createApiClient({
baseURL: 'https://api.example.com',
headers: {
'Authorization': 'Bearer token'
},
withCredentials: true, // Enable cookies
withXSRFToken: true, // Enable XSRF token handling
// CSRF token refresh endpoint
csrfRefreshEndpoint: '/sanctum/csrf-cookie',
// Global handlers
onBeforeRequest: async (config) => {
// Modify requests globally
const token = localStorage.getItem('token');
config.headers.Authorization = `Bearer ${token}`;
return config;
},
onError: (error) => {
// Global error handler
console.error('API Error:', error.message);
},
onZodError: (issues) => {
// Handle validation errors
console.error('Validation errors:', issues);
},
queries: { /* ... */ },
mutations: { /* ... */ }
});
```
## 🔧 Advanced Features
### Nested Structure
Organize endpoints hierarchically for better code organization:
```typescript
import { createApiClient, defineQuery, defineMutation } from 'vue-api-kit';
import { z } from 'zod';
const api = createApiClient({
baseURL: 'https://api.example.com',
queries: {
users: {
getAll: defineQuery({
path: '/users',
response: z.array(z.object({ id: z.number(), name: z.string() }))
}),
getById: defineQuery({
path: '/users/{id}',
params: z.object({ id: z.number() }),
response: z.object({ id: z.number(), name: z.string() })
}),
search: defineQuery({
method: 'POST',
path: '/users/search',
data: z.object({ query: z.string() }),
response: z.array(z.object({ id: z.number(), name: z.string() }))
})
},
posts: {
getAll: defineQuery({
path: '/posts',
response: z.array(z.object({ id: z.number(), title: z.string() }))
}),
getById: defineQuery({
path: '/posts/{id}',
params: z.object({ id: z.number() }),
response: z.object({ id: z.number(), title: z.string() })
})
}
},
mutations: {
users: {
create: defineMutation({
method: 'POST',
path: '/users',
data: z.object({ name: z.string(), email: z.string() }),
response: z.object({ id: z.number(), name: z.string() })
}),
update: defineMutation({
method: 'PUT',
path: '/users/{id}',
params: z.object({ id: z.number() }),
data: z.object({ name: z.string() }),
response: z.object({ id: z.number(), name: z.string() })
}),
delete: defineMutation({
method: 'DELETE',
path: '/users/{id}',
params: z.object({ id: z.number() })
})
}
}
});
// Usage
api.query.users.getAll()
api.mutation.users.create()
```
**Benefits:** Better organization, namespace separation, improved readability, scalability.
### Modular API Definitions
Split your API definitions across multiple files:
**user-api.ts**
```typescript
import { defineQuery, defineMutation } from 'vue-api-kit';
import { z } from 'zod';
export const userQueries = {
getUsers: defineQuery({
path: '/users',
response: z.array(z.object({ id: z.number(), name: z.string() }))
}),
getUser: defineQuery({
path: '/users/{id}',
params: z.object({ id: z.number() }),
response: z.object({ id: z.number(), name: z.string() })
})
};
export const userMutations = {
createUser: defineMutation({
method: 'POST',
path: '/users',
data: z.object({ name: z.string(), email: z.string() }),
response: z.object({ id: z.number(), name: z.string() })
})
};
```
**api.ts**
```typescript
import { createApiClient, mergeQueries, mergeMutations } from 'vue-api-kit';
import { userQueries, userMutations } from './user-api';
import { postQueries, postMutations } from './post-api';
export const api = createApiClient({
baseURL: 'https://api.example.com',
queries: mergeQueries(userQueries, postQueries),
mutations: mergeMutations(userMutations, postMutations)
});
```
**Benefits:** Separation of concerns, reusability, team collaboration, full type safety.
### Request Interceptors
Add interceptors at global, definition, or runtime level:
```typescript
// 1. Global interceptor
const api = createApiClient({
baseURL: 'https://api.example.com',
onBeforeRequest: async (config) => {
config.headers.Authorization = `Bearer ${getToken()}`;
return config;
}
});
// 2. Definition-level interceptor
queries: {
getUser: {
path: '/users/{id}',
onBeforeRequest: async (config) => {
config.headers['X-Custom-Header'] = 'value';
return config;
}
}
}
// 3. Runtime interceptor
const { result } = api.query.getUser({
params: { id: 1 },
onBeforeRequest: async (config) => {
config.headers.Authorization = `Bearer ${await refreshToken()}`;
return config;
}
});
```
**Execution order:** Global → Definition → Runtime
### File Upload
Upload files with multipart/form-data support:
```typescript
mutations: {
uploadImage: {
method: 'POST',
path: '/upload',
isMultipart: true,
// Optional: Laravel-friendly boolean serialization in multipart (true => "1", false => "0")
multipartBooleanStyle: 'numeric',
response: z.object({ url: z.string() })
}
}
// Usage
const { mutate, uploadProgress } = api.mutation.uploadImage({
onUploadProgress: (progress) => console.log(`${progress}%`)
});
await mutate({ data: { file, name: 'avatar.jpg' } });
```
**Nested objects in multipart:**
```typescript
await mutate({
data: {
name: 'Product',
image: {
file: file, // Sent as: image[file]
file_url: 'url' // Sent as: image[file_url]
}
}
});
```
### CSRF Protection
Built-in CSRF token protection (Laravel Sanctum compatible):
```typescript
const api = createApiClient({
baseURL: 'https://api.example.com',
withCredentials: true, // Enable cookies
withXSRFToken: true, // Enable XSRF token handling
csrfRefreshEndpoint: '/sanctum/csrf-cookie', // Refresh endpoint
mutations: { /* ... */ }
});
```
**How it works:**
1. Axios automatically reads `XSRF-TOKEN` cookie
2. Sends it as `X-XSRF-TOKEN` header
3. On 403/419 errors, refreshes CSRF token automatically
4. Retries the original request
**Laravel CORS config:**
```php
// config/cors.php
'supports_credentials' => true,
'allowed_origins' => ['http://localhost:5173'],
```
## 📝 License
MIT
## 👤 Author
**MelvishNiz** - [GitHub](https://github.com/MelvishNiz)