{"id":40797624,"url":"https://github.com/qualisero/openapi-endpoint","last_synced_at":"2026-01-21T20:40:06.218Z","repository":{"id":318267519,"uuid":"1070557541","full_name":"qualisero/openapi-endpoint","owner":"qualisero","description":"Exposes type-safe API endpoint functions to Vue components using openapi.json and TanStack","archived":false,"fork":false,"pushed_at":"2025-12-21T12:46:48.000Z","size":529,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-23T03:57:12.377Z","etag":null,"topics":["openapi","rest-api","tanstack-query","vue","vuejs"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/qualisero.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"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-10-06T05:42:40.000Z","updated_at":"2025-12-21T12:46:51.000Z","dependencies_parsed_at":null,"dependency_job_id":"1cee9c2c-f4d7-46bb-927e-9bd57025574e","html_url":"https://github.com/qualisero/openapi-endpoint","commit_stats":null,"previous_names":["qualisero/openapi-endpoint"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/qualisero/openapi-endpoint","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qualisero%2Fopenapi-endpoint","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qualisero%2Fopenapi-endpoint/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qualisero%2Fopenapi-endpoint/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qualisero%2Fopenapi-endpoint/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/qualisero","download_url":"https://codeload.github.com/qualisero/openapi-endpoint/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qualisero%2Fopenapi-endpoint/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28642220,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-21T18:04:35.752Z","status":"ssl_error","status_checked_at":"2026-01-21T18:03:55.054Z","response_time":86,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: 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":["openapi","rest-api","tanstack-query","vue","vuejs"],"created_at":"2026-01-21T20:40:05.204Z","updated_at":"2026-01-21T20:40:06.172Z","avatar_url":"https://github.com/qualisero.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# OpenApiEndpoint\n\n[![npm version](https://badge.fury.io/js/@qualisero%2Fopenapi-endpoint.svg?refresh=1763799820)](https://badge.fury.io/js/@qualisero%2Fopenapi-endpoint)\n[![CI](https://github.com/qualisero/openapi-endpoint/workflows/CI/badge.svg)](https://github.com/qualisero/openapi-endpoint/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n[![npm bundle size](https://img.shields.io/bundlephobia/minzip/@qualisero/openapi-endpoint)](https://bundlephobia.com/package/@qualisero/openapi-endpoint)\n\nTurns your `openapi.json` into typesafe API composables using Vue Query (TanStack Query): guaranteeing that your frontend and backend share the same contract.\n\n## Overview\n\nLet's you get TanStack Vue Query composables that enforce consistency (name of endpoints, typing) with your API's `openapi.json` file:\n\n```typescript\nconst { data, isLoading } = api.useQuery(OperationId.getPet, { petId: '123' })\n\nconst createPetMutation = api.useMutation(OperationId.createPet)\ncreatePetMutation.mutate({ data: { name: 'Fluffy', species: 'cat' } })\n```\n\n## Installation\n\n```bash\nnpm install @qualisero/openapi-endpoint\n```\n\n## Code Generation\n\nThis package includes a command-line tool to generate TypeScript types and operation definitions from your OpenAPI specification:\n\n```bash\n# Generate from local file\nnpx @qualisero/openapi-endpoint ./api/openapi.json ./src/generated\n\n# Generate from remote URL\nnpx @qualisero/openapi-endpoint https://api.example.com/openapi.json ./src/api\n```\n\nThis will generate two files in your specified output directory:\n\n- `openapi-types.ts` - TypeScript type definitions for your API\n- `api-operations.ts` - Operation definitions combining metadata and types\n\n## Usage\n\n### 1. Initialize the package\n\n```typescript\n// api/init.ts\nimport { useOpenApi } from '@qualisero/openapi-endpoint'\nimport axios from 'axios'\n\n// Import your auto-generated operations (includes both metadata and types)\nimport { OperationId, openApiOperations, type OpenApiOperations } from './generated/api-operations'\n\n// Create axios instance\nconst axiosInstance = axios.create({\n  baseURL: 'https://api.example.com',\n})\n\n// Initialize the package with the auto-generated operations\nconst api = useOpenApi\u003cOpenApiOperations\u003e({\n  operations: openApiOperations,\n  axios: axiosInstance,\n})\n\n// Export for use in other parts of your application\nexport { api, OperationId }\n```\n\n### 2. Use the API in your components\n\n```typescript\n// In your Vue components\nimport { api, OperationId } from './api/init'\n\n// Use queries for GET operations\nconst { data: pets, isLoading } = api.useQuery(OperationId.listPets)\nconst { data: pet } = api.useQuery(OperationId.getPet, { petId: '123' })\n\n// Use mutations for POST/PUT/PATCH/DELETE operations\nconst createPetMutation = api.useMutation(OperationId.createPet)\n\n// Execute mutations\nawait createPetMutation.mutateAsync({\n  data: { name: 'Fluffy', species: 'cat' },\n})\n```\n\n## Advanced Usage\n\n### Reactive Query Parameters\n\nThe library supports type-safe, reactive query parameters that automatically trigger refetches when their values change:\n\n```typescript\nimport { ref, computed } from 'vue'\nimport { api, OperationId } from './api/init'\n\n// Static query parameters\nconst { data: pets } = api.useQuery(OperationId.listPets, {\n  queryParams: { limit: 10 },\n})\n// Results in: GET /pets?limit=10\n\n// Reactive query parameters with computed\nconst limit = ref(10)\nconst status = ref\u003c'available' | 'pending' | 'sold'\u003e('available')\n\nconst petsQuery = api.useQuery(OperationId.listPets, {\n  queryParams: computed(() =\u003e ({\n    limit: limit.value,\n    status: status.value,\n  })),\n})\n\n// When limit or status changes, query automatically refetches\nlimit.value = 20\nstatus.value = 'pending'\n// Query refetches with: GET /pets?limit=20\u0026status=pending\n\n// Combine with path parameters\nconst userPetsQuery = api.useQuery(\n  OperationId.listUserPets,\n  computed(() =\u003e ({ userId: userId.value })),\n  {\n    queryParams: computed(() =\u003e ({\n      includeArchived: includeArchived.value,\n    })),\n  },\n)\n// Results in: GET /users/user-123/pets?includeArchived=false\n```\n\n**Key Features:**\n\n- **Type-safe**: Query parameters are typed based on your OpenAPI specification\n- **Reactive**: Supports `ref`, `computed`, and function-based values\n- **Automatic refetch**: Changes to query params trigger automatic refetch via TanStack Query's key mechanism\n- **Backward compatible**: Works alongside existing `axiosOptions.params`\n\n### Automatic Operation Type Detection with `api.useEndpoint`\n\nThe `api.useEndpoint` method automatically detects whether an operation is a query (GET/HEAD/OPTIONS) or mutation (POST/PUT/PATCH/DELETE) based on the HTTP method defined in your OpenAPI specification:\n\n```typescript\nimport { ref, computed } from 'vue'\nimport { api, OperationId } from './api/init'\n\n// Automatically becomes a query for GET operations\nconst listEndpoint = api.useEndpoint(OperationId.listPets)\n// TypeScript knows this has query properties like .data, .isLoading, .refetch()\n\n// Automatically becomes a mutation for POST operations\nconst createEndpoint = api.useEndpoint(OperationId.createPet)\n// TypeScript knows this has mutation properties like .mutate(), .mutateAsync()\n\n// Use the endpoints according to their detected type\nconst petData = listEndpoint.data // Query data\nawait createEndpoint.mutateAsync({ data: { name: 'Fluffy' } }) // Mutation execution\n```\n\n### Automatic Cache Management and Refetching\n\nBy default, mutations automatically:\n\n1. Update cache for matching queries with returned data\n2. Invalidate them to trigger a reload\n3. Invalidate matching list endpoints\n\n```typescript\n// Default behavior: automatic cache management\nconst createPet = api.useMutation(OperationId.createPet)\n// No additional configuration needed - cache management is automatic\n\n// Manual control over cache invalidation\nconst updatePet = api.useMutation(OperationId.updatePet, {\n  dontInvalidate: true, // Disable automatic invalidation\n  dontUpdateCache: true, // Disable automatic cache updates\n  invalidateOperations: [OperationId.listPets], // Manually specify operations to invalidate\n})\n\n// Refetch specific endpoints after mutation\nconst petListQuery = api.useQuery(OperationId.listPets)\nconst createPetWithRefetch = api.useMutation(OperationId.createPet, {\n  refetchEndpoints: [petListQuery], // Manually refetch these endpoints\n})\n```\n\n### File Upload Support with Multipart/Form-Data\n\nThe library supports file uploads through endpoints that accept `multipart/form-data` content type. For these endpoints, you can pass either a `FormData` object or the schema-defined object structure:\n\n```typescript\n// Example file upload endpoint usage\nasync function uploadPetPicture(petId: string, file: File) {\n  const formData = new FormData()\n  formData.append('file', file)\n\n  const uploadMutation = api.useMutation(OperationId.uploadPetPic, { petId })\n\n  return uploadMutation.mutateAsync({\n    data: formData,\n    axiosOptions: {\n      headers: {\n        'Content-Type': 'multipart/form-data',\n      },\n    },\n  })\n}\n\n// Alternative: using the object structure (if your API supports binary strings)\nasync function uploadPetPictureAsString(petId: string, binaryData: string) {\n  const uploadMutation = api.useMutation(OperationId.uploadPetPic, { petId })\n\n  return uploadMutation.mutateAsync({\n    data: {\n      file: binaryData, // Binary data as string\n    },\n  })\n}\n\n// Complete example with error handling and cache invalidation\nasync function handleFileUpload(event: Event, petId: string) {\n  const files = (event.target as HTMLInputElement).files\n  if (!files || files.length === 0) return\n\n  const file = files[0]\n  const formData = new FormData()\n  formData.append('file', file)\n\n  const uploadMutation = api.useMutation(\n    OperationId.uploadPetPic,\n    { petId },\n    {\n      invalidateOperations: [OperationId.getPet, OperationId.listPets],\n      onSuccess: (data) =\u003e {\n        console.log('Upload successful:', data)\n      },\n      onError: (error) =\u003e {\n        console.error('Upload failed:', error)\n      },\n    },\n  )\n\n  try {\n    await uploadMutation.mutateAsync({ data: formData })\n  } catch (error) {\n    console.error('Upload error:', error)\n  }\n}\n```\n\n### Reactive Enabling/Disabling Based on Path Parameters\n\nOne powerful feature is chaining queries where one query provides the parameters for another:\n\n```typescript\nimport { ref, computed } from 'vue'\n\n// First query to get user information\nconst userQuery = api.useQuery(OperationId.getUser, { userId: 123 })\n\n// Second query that depends on the first query's result\nconst userPetsQuery = api.useQuery(\n  OperationId.listUserPets,\n  computed(() =\u003e ({\n    userId: userQuery.data.value?.id, // Chain: use ID from first query\n  })),\n)\n\n// Reactive parameter example\nconst selectedPetId = ref\u003cstring | undefined\u003e(undefined)\n\n// Query automatically enables/disables based on parameter availability\nconst petQuery = api.useQuery(\n  OperationId.getPet,\n  computed(() =\u003e ({ petId: selectedPetId.value })),\n)\n\n// Query is automatically disabled when petId is null/undefined\nconsole.log(petQuery.isEnabled.value) // false when selectedPetId.value is null\n\n// Enable the query by setting the parameter\nselectedPetId.value = '123'\nconsole.log(petQuery.isEnabled.value) // true when selectedPetId.value is set\n\n// Complex conditional enabling\nconst userId = ref\u003cstring\u003e('user1')\nconst shouldFetchPets = ref(true)\n\nconst userPetsQuery = api.useQuery(\n  OperationId.listUserPets,\n  computed(() =\u003e ({ userId: userId.value })),\n  {\n    enabled: computed(\n      () =\u003e shouldFetchPets.value, // Additional business logic\n    ),\n  },\n)\n```\n\n### Reactive Query Parameters with Refs\n\n```typescript\nimport { ref } from 'vue'\n\n// Use reactive query parameters\nconst limit = ref(10)\nconst petsQuery = api.useQuery(OperationId.listPets, {\n  queryParams: { limit: limit.value },\n})\n\n// When limit changes, the query automatically refetches\nlimit.value = 20 // Query refetches with new parameter\n// Results in: GET /pets?limit=20\n```\n\n## API Documentation\n\nFull API documentation is available at [https://qualisero.github.io/openapi-endpoint/](https://qualisero.github.io/openapi-endpoint/). The documentation includes detailed information about all methods, types, and configuration options.\n\n## Contributing\n\nContributions are welcome! Please read our contributing guidelines and ensure all tests pass.\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqualisero%2Fopenapi-endpoint","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fqualisero%2Fopenapi-endpoint","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqualisero%2Fopenapi-endpoint/lists"}