{"id":35201561,"url":"https://github.com/melvishniz/vue-api-kit","last_synced_at":"2026-02-20T06:02:46.516Z","repository":{"id":329097247,"uuid":"1116551957","full_name":"MelvishNiz/vue-api-kit","owner":"MelvishNiz","description":"A powerful and type-safe API client for Vue 3 applications with built-in validation using Zod.","archived":false,"fork":false,"pushed_at":"2026-02-13T10:18:07.000Z","size":1254,"stargazers_count":3,"open_issues_count":9,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-13T22:46:45.912Z","etag":null,"topics":["api","api-client","axios","http","rest","sdk","typescript","vue","vue3","zod"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/vue-api-kit","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/MelvishNiz.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-15T03:35:56.000Z","updated_at":"2026-02-13T10:17:13.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/MelvishNiz/vue-api-kit","commit_stats":null,"previous_names":["melvishniz/vue-api-kit"],"tags_count":29,"template":false,"template_full_name":null,"purl":"pkg:github/MelvishNiz/vue-api-kit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MelvishNiz%2Fvue-api-kit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MelvishNiz%2Fvue-api-kit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MelvishNiz%2Fvue-api-kit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MelvishNiz%2Fvue-api-kit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MelvishNiz","download_url":"https://codeload.github.com/MelvishNiz/vue-api-kit/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MelvishNiz%2Fvue-api-kit/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29642907,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-20T05:21:04.652Z","status":"ssl_error","status_checked_at":"2026-02-20T05:21:04.238Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","api-client","axios","http","rest","sdk","typescript","vue","vue3","zod"],"created_at":"2025-12-29T11:21:57.127Z","updated_at":"2026-02-20T06:02:46.510Z","avatar_url":"https://github.com/MelvishNiz.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# 🚀 vue-api-kit\n\n[![NPM Version](https://img.shields.io/npm/v/vue-api-kit.svg?style=flat-square)](https://www.npmjs.com/package/vue-api-kit)\n[![Install Size](https://img.shields.io/badge/dynamic/json?url=https://packagephobia.com/v2/api.json?p=vue-api-kit\u0026query=$.install.pretty\u0026label=install%20size\u0026style=flat-square)](https://packagephobia.now.sh/result?p=vue-api-kit)\n[![Bundle Size](https://img.shields.io/bundlephobia/minzip/vue-api-kit?style=flat-square)](https://bundlephobia.com/result?p=vue-api-kit)\n[![NPM Downloads](https://img.shields.io/npm/dm/vue-api-kit.svg?style=flat-square)](https://npm-stat.com/charts.html?package=vue-api-kit)\n[![CI Status](https://img.shields.io/github/actions/workflow/status/MelvishNiz/vue-api-kit/release.yml?label=CI\u0026logo=github\u0026style=flat-square)](https://github.com/MelvishNiz/vue-api-kit/actions)\n[![License](https://img.shields.io/npm/l/vue-api-kit.svg?style=flat-square)](https://github.com/MelvishNiz/vue-api-kit/blob/main/LICENSE)\n\nA powerful and type-safe API client for Vue 3 applications with built-in validation using Zod.\n\n## 📋 Table of Contents\n\n- [Installation](#-installation)\n- [Quick Start](#-quick-start)\n- [Core Features](#-core-features)\n- [Basic Usage](#-basic-usage)\n  - [Queries (GET)](#queries-get)\n  - [Queries (POST)](#queries-post)\n  - [Mutations (POST/PUT/DELETE)](#mutations-postputdelete)\n- [Configuration](#-configuration)\n- [Advanced Features](#-advanced-features)\n  - [Nested Structure](#nested-structure)\n  - [Modular API Definitions](#modular-api-definitions)\n  - [Request Interceptors](#request-interceptors)\n  - [File Upload](#file-upload)\n  - [CSRF Protection](#csrf-protection)\n- [License](#-license)\n\n## 📦 Installation\n\n```bash\nnpm install vue-api-kit\n```\n\n## ⚡ Quick Start\n\n```typescript\nimport { createApiClient } from 'vue-api-kit';\nimport { z } from 'zod';\n\n// Define your API client\nconst api = createApiClient({\n  baseURL: 'https://api.example.com',\n  queries: {\n    getUsers: {\n      path: '/users',\n      response: z.array(z.object({\n        id: z.number(),\n        name: z.string(),\n        email: z.string()\n      }))\n    },\n    getUser: {\n      path: '/users/{id}',\n      params: z.object({ id: z.number() }),\n      response: z.object({\n        id: z.number(),\n        name: z.string(),\n        email: z.string()\n      })\n    }\n  },\n  mutations: {\n    createUser: {\n      method: 'POST',\n      path: '/users',\n      data: z.object({\n        name: z.string(),\n        email: z.string().email()\n      }),\n      response: z.object({\n        id: z.number(),\n        name: z.string(),\n        email: z.string()\n      })\n    }\n  }\n});\n```\n\nUse in your Vue components:\n\n```vue\n\u003cscript setup lang=\"ts\"\u003e\nimport { api } from './api';\n\n// Query - auto-loads on mount\nconst { result, isLoading, errorMessage } = api.query.getUsers();\n\n// Mutation\nconst { mutate, isLoading: creating } = api.mutation.createUser();\n\nasync function handleCreate() {\n  await mutate({ name: 'John', email: 'john@example.com' });\n}\n\u003c/script\u003e\n\n\u003ctemplate\u003e\n  \u003cdiv v-if=\"isLoading\"\u003eLoading...\u003c/div\u003e\n  \u003cdiv v-else-if=\"errorMessage\"\u003eError: {{ errorMessage }}\u003c/div\u003e\n  \u003cul v-else\u003e\n    \u003cli v-for=\"user in result\" :key=\"user.id\"\u003e{{ user.name }}\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/template\u003e\n```\n\n## 🎯 Core Features\n\n- ✅ **Type-Safe** - Full TypeScript support with automatic type inference\n- ✅ **Zod Validation** - Built-in request/response validation\n- ✅ **Vue 3 Composition API** - Reactive state management\n- ✅ **Lightweight** - ~7kB minified (2.2kB gzipped)\n- ✅ **Auto Loading States** - Built-in loading, error, and success states\n- ✅ **Path Parameters** - Automatic path parameter replacement (`/users/{id}`)\n- ✅ **Debouncing** - Built-in request debouncing\n- ✅ **POST Queries** - Support both GET and POST for data fetching\n- ✅ **File Upload** - Multipart/form-data with nested objects\n- ✅ **CSRF Protection** - Automatic token refresh (Laravel Sanctum compatible)\n- ✅ **Modular** - Split API definitions across files\n- ✅ **Nested Structure** - Organize endpoints hierarchically\n- ✅ **Tree-Shakeable** - Only bundles what you use\n\n## 📖 Basic Usage\n\n### Queries (GET)\n\nUse queries to fetch data. They automatically load on component mount:\n\n```vue\n\u003cscript setup lang=\"ts\"\u003e\nimport { api } from './api';\nimport { ref } from 'vue';\n\n// Simple query - automatically loads data on mount\nconst { result, isLoading, errorMessage } = api.query.getUsers();\n\n// Query with parameters - reactive to parameter changes\nconst userId = ref(1);\nconst { result: user, refetch } = api.query.getUser({\n  params: { id: userId }\n});\n\n// Query with options - customize behavior\nconst { result: data } = api.query.getUsers({\n  loadOnMount: true,\n  debounce: 300,\n  onResult: (data) =\u003e console.log('Loaded:', data),\n  onError: (error) =\u003e console.error('Error:', error)\n});\n\u003c/script\u003e\n\n\u003ctemplate\u003e\n  \u003cdiv v-if=\"isLoading\"\u003eLoading...\u003c/div\u003e\n  \u003cdiv v-else-if=\"errorMessage\"\u003eError: {{ errorMessage }}\u003c/div\u003e\n  \u003cul v-else\u003e\n    \u003cli v-for=\"user in result\" :key=\"user.id\"\u003e{{ user.name }}\u003c/li\u003e\n  \u003c/ul\u003e\n\u003c/template\u003e\n```\n\n### Queries (POST)\n\nPOST queries are perfect for complex searches with filters:\n\n```typescript\n// API definition\nqueries: {\n  searchUsers: {\n    method: 'POST',\n    path: '/users/search',\n    data: z.object({\n      query: z.string(),\n      filters: z.object({\n        active: z.boolean().optional(),\n        role: z.string().optional()\n      }).optional()\n    }),\n    response: z.array(z.object({ id: z.number(), name: z.string() }))\n  }\n}\n```\n\n```vue\n\u003cscript setup lang=\"ts\"\u003e\nconst searchTerm = ref('');\nconst { result, isLoading, refetch } = api.query.searchUsers({\n  data: {\n    query: searchTerm.value,\n    filters: { active: true }\n  },\n  loadOnMount: false\n});\n\u003c/script\u003e\n\n\u003ctemplate\u003e\n  \u003cinput v-model=\"searchTerm\" @keyup.enter=\"refetch\" /\u003e\n  \u003cbutton @click=\"refetch\" :disabled=\"isLoading\"\u003eSearch\u003c/button\u003e\n  \u003cdiv v-if=\"isLoading\"\u003eSearching...\u003c/div\u003e\n  \u003cdiv v-else-if=\"result\"\u003e\n    \u003cdiv v-for=\"user in result\" :key=\"user.id\"\u003e{{ user.name }}\u003c/div\u003e\n  \u003c/div\u003e\n\u003c/template\u003e\n```\n\n### Mutations (POST/PUT/DELETE)\n\n```vue\n\u003cscript setup lang=\"ts\"\u003e\nconst { mutate, isLoading, result, errorMessage } = api.mutation.createUser({\n  onResult: (data) =\u003e console.log('Created:', data),\n  onError: (error) =\u003e console.error('Error:', error)\n});\n\nconst name = ref('');\nconst email = ref('');\n\nasync function handleSubmit() {\n  await mutate({ name: name.value, email: email.value });\n}\n\u003c/script\u003e\n\n\u003ctemplate\u003e\n  \u003cform @submit.prevent=\"handleSubmit\"\u003e\n    \u003cinput v-model=\"name\" placeholder=\"Name\" /\u003e\n    \u003cinput v-model=\"email\" placeholder=\"Email\" /\u003e\n    \u003cbutton type=\"submit\" :disabled=\"isLoading\"\u003e\n      {{ isLoading ? 'Creating...' : 'Create User' }}\n    \u003c/button\u003e\n    \u003cp v-if=\"errorMessage\" class=\"error\"\u003e{{ errorMessage }}\u003c/p\u003e\n  \u003c/form\u003e\n\u003c/template\u003e\n```\n\n## ⚙️ Configuration\n\n```typescript\nconst api = createApiClient({\n  baseURL: 'https://api.example.com',\n  headers: {\n    'Authorization': 'Bearer token'\n  },\n  withCredentials: true,  // Enable cookies\n  withXSRFToken: true,    // Enable XSRF token handling\n\n  // CSRF token refresh endpoint\n  csrfRefreshEndpoint: '/sanctum/csrf-cookie',\n\n  // Global handlers\n  onBeforeRequest: async (config) =\u003e {\n    // Modify requests globally\n    const token = localStorage.getItem('token');\n    config.headers.Authorization = `Bearer ${token}`;\n    return config;\n  },\n\n  onError: (error) =\u003e {\n    // Global error handler\n    console.error('API Error:', error.message);\n  },\n\n  onZodError: (issues) =\u003e {\n    // Handle validation errors\n    console.error('Validation errors:', issues);\n  },\n\n  queries: { /* ... */ },\n  mutations: { /* ... */ }\n});\n```\n\n## 🔧 Advanced Features\n\n### Nested Structure\n\nOrganize endpoints hierarchically for better code organization:\n\n```typescript\nimport { createApiClient, defineQuery, defineMutation } from 'vue-api-kit';\nimport { z } from 'zod';\n\nconst api = createApiClient({\n  baseURL: 'https://api.example.com',\n  queries: {\n    users: {\n      getAll: defineQuery({\n        path: '/users',\n        response: z.array(z.object({ id: z.number(), name: z.string() }))\n      }),\n      getById: defineQuery({\n        path: '/users/{id}',\n        params: z.object({ id: z.number() }),\n        response: z.object({ id: z.number(), name: z.string() })\n      }),\n      search: defineQuery({\n        method: 'POST',\n        path: '/users/search',\n        data: z.object({ query: z.string() }),\n        response: z.array(z.object({ id: z.number(), name: z.string() }))\n      })\n    },\n    posts: {\n      getAll: defineQuery({\n        path: '/posts',\n        response: z.array(z.object({ id: z.number(), title: z.string() }))\n      }),\n      getById: defineQuery({\n        path: '/posts/{id}',\n        params: z.object({ id: z.number() }),\n        response: z.object({ id: z.number(), title: z.string() })\n      })\n    }\n  },\n  mutations: {\n    users: {\n      create: defineMutation({\n        method: 'POST',\n        path: '/users',\n        data: z.object({ name: z.string(), email: z.string() }),\n        response: z.object({ id: z.number(), name: z.string() })\n      }),\n      update: defineMutation({\n        method: 'PUT',\n        path: '/users/{id}',\n        params: z.object({ id: z.number() }),\n        data: z.object({ name: z.string() }),\n        response: z.object({ id: z.number(), name: z.string() })\n      }),\n      delete: defineMutation({\n        method: 'DELETE',\n        path: '/users/{id}',\n        params: z.object({ id: z.number() })\n      })\n    }\n  }\n});\n\n// Usage\napi.query.users.getAll()\napi.mutation.users.create()\n```\n\n**Benefits:** Better organization, namespace separation, improved readability, scalability.\n\n### Modular API Definitions\n\nSplit your API definitions across multiple files:\n\n**user-api.ts**\n```typescript\nimport { defineQuery, defineMutation } from 'vue-api-kit';\nimport { z } from 'zod';\n\nexport const userQueries = {\n  getUsers: defineQuery({\n    path: '/users',\n    response: z.array(z.object({ id: z.number(), name: z.string() }))\n  }),\n  getUser: defineQuery({\n    path: '/users/{id}',\n    params: z.object({ id: z.number() }),\n    response: z.object({ id: z.number(), name: z.string() })\n  })\n};\n\nexport const userMutations = {\n  createUser: defineMutation({\n    method: 'POST',\n    path: '/users',\n    data: z.object({ name: z.string(), email: z.string() }),\n    response: z.object({ id: z.number(), name: z.string() })\n  })\n};\n```\n\n**api.ts**\n```typescript\nimport { createApiClient, mergeQueries, mergeMutations } from 'vue-api-kit';\nimport { userQueries, userMutations } from './user-api';\nimport { postQueries, postMutations } from './post-api';\n\nexport const api = createApiClient({\n  baseURL: 'https://api.example.com',\n  queries: mergeQueries(userQueries, postQueries),\n  mutations: mergeMutations(userMutations, postMutations)\n});\n```\n\n**Benefits:** Separation of concerns, reusability, team collaboration, full type safety.\n\n### Request Interceptors\n\nAdd interceptors at global, definition, or runtime level:\n\n```typescript\n// 1. Global interceptor\nconst api = createApiClient({\n  baseURL: 'https://api.example.com',\n  onBeforeRequest: async (config) =\u003e {\n    config.headers.Authorization = `Bearer ${getToken()}`;\n    return config;\n  }\n});\n\n// 2. Definition-level interceptor\nqueries: {\n  getUser: {\n    path: '/users/{id}',\n    onBeforeRequest: async (config) =\u003e {\n      config.headers['X-Custom-Header'] = 'value';\n      return config;\n    }\n  }\n}\n\n// 3. Runtime interceptor\nconst { result } = api.query.getUser({\n  params: { id: 1 },\n  onBeforeRequest: async (config) =\u003e {\n    config.headers.Authorization = `Bearer ${await refreshToken()}`;\n    return config;\n  }\n});\n```\n\n**Execution order:** Global → Definition → Runtime\n\n### File Upload\n\nUpload files with multipart/form-data support:\n\n```typescript\nmutations: {\n  uploadImage: {\n    method: 'POST',\n    path: '/upload',\n    isMultipart: true,\n    // Optional: Laravel-friendly boolean serialization in multipart (true =\u003e \"1\", false =\u003e \"0\")\n    multipartBooleanStyle: 'numeric',\n    response: z.object({ url: z.string() })\n  }\n}\n\n// Usage\nconst { mutate, uploadProgress } = api.mutation.uploadImage({\n  onUploadProgress: (progress) =\u003e console.log(`${progress}%`)\n});\n\nawait mutate({ data: { file, name: 'avatar.jpg' } });\n```\n\n**Nested objects in multipart:**\n```typescript\nawait mutate({\n  data: {\n    name: 'Product',\n    image: {\n      file: file,              // Sent as: image[file]\n      file_url: 'url'          // Sent as: image[file_url]\n    }\n  }\n});\n```\n\n### CSRF Protection\n\nBuilt-in CSRF token protection (Laravel Sanctum compatible):\n\n```typescript\nconst api = createApiClient({\n  baseURL: 'https://api.example.com',\n  withCredentials: true,              // Enable cookies\n  withXSRFToken: true,                // Enable XSRF token handling\n  csrfRefreshEndpoint: '/sanctum/csrf-cookie',  // Refresh endpoint\n  mutations: { /* ... */ }\n});\n```\n\n**How it works:**\n1. Axios automatically reads `XSRF-TOKEN` cookie\n2. Sends it as `X-XSRF-TOKEN` header\n3. On 403/419 errors, refreshes CSRF token automatically\n4. Retries the original request\n\n**Laravel CORS config:**\n```php\n// config/cors.php\n'supports_credentials' =\u003e true,\n'allowed_origins' =\u003e ['http://localhost:5173'],\n```\n\n## 📝 License\n\nMIT\n\n## 👤 Author\n\n**MelvishNiz** - [GitHub](https://github.com/MelvishNiz)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmelvishniz%2Fvue-api-kit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmelvishniz%2Fvue-api-kit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmelvishniz%2Fvue-api-kit/lists"}