UNPKG

@wisemen/vue-core-api-utils

Version:
249 lines (192 loc) 6.67 kB
--- name: getting-started description: > Install @wisemen/vue-core-api-utils, initialize apiUtilsPlugin with QueryClient config, define typed query keys interface, create API composables with error codes. type: lifecycle library: vue-core-api-utils library_version: "1.0.1" sources: - "wisemen-digital/wisemen-core:docs/packages/api-utils/pages/getting-started/installation.md" - "wisemen-digital/wisemen-core:packages/web/api-utils/src/plugin/apiUtilsPlugin.ts" - "wisemen-digital/wisemen-core:packages/web/api-utils/src/config/config.ts" --- # @wisemen/vue-core-api-utils — Getting Started Get `@wisemen/vue-core-api-utils` installed, your Vue Query plugin initialized, query keys defined, and typed composables created. ## Setup ### 1. Install the package ```bash pnpm install @wisemen/vue-core-api-utils @tanstack/vue-query neverthrow vue ``` ### 2. Define your query keys Create a TypeScript interface that maps query keys to their response types and parameters: ```typescript // src/types/queryKey.type.ts export interface ProjectQueryKeys { // Single entity query contactDetail: { entity: Contact params: { contactUuid: string } } // List query with offset pagination contactList: { entity: Contact[] params: { page: number; limit: number; search?: string } } // List query with keyset pagination contactListKeyset: { entity: Contact[] params: { limit: number; key?: string } } } ``` Every key must have both `entity` (response type) and `params` (required parameters). ### 3. Initialize the plugin in your main.ts ```typescript // main.ts import { createApp } from 'vue' import { apiUtilsPlugin } from '@wisemen/vue-core-api-utils' import App from './App.vue' const app = createApp(App) app.use(apiUtilsPlugin({ defaultOptions: { queries: { staleTime: 1000 * 60 * 5, // 5 minutes retry: 1, }, }, })) app.mount('#app') ``` The `apiUtilsPlugin` function creates a QueryClient with your config and handles @tanstack/vue-query setup internally. ### 4. Create your API composables ```typescript // src/api/index.ts import type { ApiResult as ApiUtilsApiResult, KeysetPaginationResult as ApiUtilsKeysetPaginationResult, OffsetPaginationResult as ApiUtilsOffsetPaginationResult, } from '@wisemen/vue-core-api-utils' import { createApiUtils } from '@wisemen/vue-core-api-utils' import type { ProjectQueryKeys } from '@/types/queryKey.type' // Define your error codes export type ERROR_KEYS = 'NOT_FOUND' | 'UNAUTHORIZED' | 'NETWORK_ERROR' | 'VALIDATION_ERROR' // Create factory with your types export const { useKeysetInfiniteQuery, useMutation, useOffsetInfiniteQuery, useQuery, usePrefetchKeysetInfiniteQuery, usePrefetchOffsetInfiniteQuery, usePrefetchQuery, useQueryClient, } = createApiUtils<ProjectQueryKeys, ERROR_KEYS>() // Export typed result types export type ApiResult<T> = ApiUtilsApiResult<T, ERROR_KEYS> export type OffsetPaginationResult<T> = ApiUtilsOffsetPaginationResult<T, ERROR_KEYS> export type KeysetPaginationResult<T> = ApiUtilsKeysetPaginationResult<T, ERROR_KEYS> ``` ## Core Patterns ### Create a detail query composable ```typescript // src/composables/useContactDetail.ts import { computed } from 'vue' import { useQuery } from '@/api' import { ContactService } from '@/services' export function useContactDetail(contactUuid: string) { return useQuery('contactDetail', { params: { contactUuid: computed(() => contactUuid) }, queryFn: () => ContactService.getByUuid(contactUuid), staleTime: 1000 * 60 * 5, }) } ``` Parameters must be computed refs so the query watches changes and refetches automatically. ### Create a mutation composable ```typescript // src/composables/useCreateContact.ts import { useMutation } from '@/api' import { ContactService } from '@/services' export function useCreateContact() { return useMutation({ queryFn: async (options: { body: ContactCreateForm }) => { return await ContactService.create(options.body) }, queryKeysToInvalidate: { contactList: {}, // Invalidate all contactList queries after success }, }) } ``` Every mutation should list which queries to invalidate via `queryKeysToInvalidate`. ### Use composables in components ```vue <script setup lang="ts"> import { useContactDetail } from '@/composables' const props = defineProps<{ contactUuid: string }>() const { result, refetch } = useContactDetail(props.contactUuid) </script> <template> <div> <div v-if="result.isLoading()">Loading...</div> <div v-else-if="result.isOk()"> Name: {{ result.getValue().name }} </div> <div v-else-if="result.isErr()"> Error: {{ result.getError().detail }} </div> <button @click="refetch">Retry</button> </div> </template> ``` All queries and mutations return `AsyncResult` with three states: loading, ok, and err. ## Common Mistakes ### CRITICAL: Forget to initialize apiUtilsPlugin with QueryClient config ```typescript // ❌ Wrong: plugin not initialized const app = createApp(App) app.mount('#app') // Throws: "[api-utils] QueryClient not available..." ``` ```typescript // ✅ Correct: plugin initialized with config const app = createApp(App) app.use(apiUtilsPlugin({ defaultOptions: { queries: { staleTime: 1000 * 60 * 5 }, }, })) app.mount('#app') ``` If you skip the plugin, `createApiUtils()` has no QueryClient and throws immediately. Source: `src/config/config.ts` — `getQueryClient()` assertion ### HIGH: Define query keys interface without strict entity/params structure ```typescript // ❌ Wrong: inconsistent structure export interface ProjectQueryKeys { contactDetail: { entity: Contact } // Missing params! contactList: Contact[] // Should wrap in { entity, params } } ``` ```typescript // ✅ Correct: every key has entity and params export interface ProjectQueryKeys { contactDetail: { entity: Contact params: { contactUuid: string } } contactList: { entity: Contact[] params: { page: number; limit: number } } } ``` Query keys without proper structure prevent the factory from typing composables correctly and cause runtime errors during query key resolution. Source: `docs/packages/api-utils/pages/getting-started/installation.md` Section 3 ## You're all set! You now have: - ✅ Plugin initialized with Vue Query - ✅ Query keys defined with types - ✅ API composables created - ✅ Error codes enumerated Head to [writing-queries](../writing-queries/SKILL.md) to fetch your first resource, or [handling-asyncresult-types](../asyncresult-handling/SKILL.md) to understand the three-state AsyncResult type.