@qualisero/openapi-endpoint 0.13.2 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +135 -236
- package/dist/cli.js +747 -202
- package/dist/index.d.ts +9 -36
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -58
- package/dist/openapi-helpers.d.ts +4 -17
- package/dist/openapi-helpers.d.ts.map +1 -1
- package/dist/openapi-helpers.js +3 -125
- package/dist/openapi-mutation.d.ts +28 -59
- package/dist/openapi-mutation.d.ts.map +1 -1
- package/dist/openapi-mutation.js +67 -83
- package/dist/openapi-query.d.ts +22 -50
- package/dist/openapi-query.d.ts.map +1 -1
- package/dist/openapi-query.js +22 -55
- package/dist/openapi-utils.d.ts +2 -1
- package/dist/openapi-utils.d.ts.map +1 -1
- package/dist/types.d.ts +223 -467
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +3 -28
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -3,22 +3,39 @@
|
|
|
3
3
|
[](https://badge.fury.io/js/@qualisero%2Fopenapi-endpoint)
|
|
4
4
|
[](https://github.com/qualisero/openapi-endpoint/actions/workflows/ci.yml)
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
|
-
[](https://bundlephobia.com/package/@qualisero%2Fopenapi-endpoint)
|
|
7
7
|
[](https://qualisero.github.io/openapi-endpoint/)
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Type-safe API composables for Vue using TanStack Query. Generate fully-typed API clients from your OpenAPI specification.
|
|
10
10
|
|
|
11
|
-
##
|
|
12
|
-
|
|
13
|
-
Let's you get TanStack Vue Query composables that enforce consistency (name of endpoints, typing) with your API's `openapi.json` file:
|
|
11
|
+
## Quick Start
|
|
14
12
|
|
|
15
13
|
```typescript
|
|
16
|
-
|
|
14
|
+
// 1. Generate types from your OpenAPI spec
|
|
15
|
+
npx @qualisero/openapi-endpoint ./api/openapi.json ./src/generated
|
|
16
|
+
|
|
17
|
+
// 2. Initialize the API client
|
|
18
|
+
import { createApiClient } from '@qualisero/openapi-endpoint'
|
|
19
|
+
import axios from 'axios'
|
|
20
|
+
|
|
21
|
+
const api = createApiClient(axios.create({ baseURL: 'https://api.example.com' }))
|
|
22
|
+
|
|
23
|
+
// 3. Use in your Vue components
|
|
24
|
+
const { data: pets, isLoading } = api.listPets.useQuery()
|
|
25
|
+
const { data: pet } = api.getPet.useQuery({ petId: '123' })
|
|
17
26
|
|
|
18
|
-
const
|
|
19
|
-
|
|
27
|
+
const createPet = api.createPet.useMutation()
|
|
28
|
+
await createPet.mutateAsync({ data: { name: 'Fluffy', species: 'cat' } })
|
|
20
29
|
```
|
|
21
30
|
|
|
31
|
+
## Features
|
|
32
|
+
|
|
33
|
+
- **Fully typed** - Operations, parameters, and responses type-checked against your OpenAPI spec
|
|
34
|
+
- **Reactive parameters** - Query params automatically refetch when values change
|
|
35
|
+
- **Automatic cache management** - Mutations invalidate and update related queries
|
|
36
|
+
- **Vue 3 + TanStack Query** - Built on proven reactive patterns
|
|
37
|
+
- **File uploads** - Support for multipart/form-data endpoints
|
|
38
|
+
|
|
22
39
|
## Installation
|
|
23
40
|
|
|
24
41
|
```bash
|
|
@@ -27,287 +44,169 @@ npm install @qualisero/openapi-endpoint
|
|
|
27
44
|
|
|
28
45
|
## Code Generation
|
|
29
46
|
|
|
30
|
-
This package includes a command-line tool to generate TypeScript types and operation definitions from your OpenAPI specification:
|
|
31
|
-
|
|
32
47
|
```bash
|
|
33
|
-
#
|
|
48
|
+
# From local file
|
|
34
49
|
npx @qualisero/openapi-endpoint ./api/openapi.json ./src/generated
|
|
35
50
|
|
|
36
|
-
#
|
|
37
|
-
npx @qualisero/openapi-endpoint https://api.example.com/openapi.json ./src/
|
|
51
|
+
# From remote URL
|
|
52
|
+
npx @qualisero/openapi-endpoint https://api.example.com/openapi.json ./src/generated
|
|
38
53
|
```
|
|
39
54
|
|
|
40
|
-
|
|
55
|
+
Generated files:
|
|
41
56
|
|
|
42
|
-
|
|
43
|
-
|
|
57
|
+
| File | Description |
|
|
58
|
+
| ------------------- | -------------------------------------------- |
|
|
59
|
+
| `api-client.ts` | `createApiClient` factory (main entry point) |
|
|
60
|
+
| `api-operations.ts` | Operations map and type helpers |
|
|
61
|
+
| `api-types.ts` | Type namespace for response/request types |
|
|
62
|
+
| `api-enums.ts` | Schema enums |
|
|
63
|
+
| `api-schemas.ts` | Schema type aliases |
|
|
64
|
+
| `openapi-types.ts` | Raw OpenAPI types |
|
|
44
65
|
|
|
45
|
-
##
|
|
66
|
+
## API Reference
|
|
46
67
|
|
|
47
|
-
###
|
|
68
|
+
### Initialization
|
|
48
69
|
|
|
49
70
|
```typescript
|
|
50
|
-
|
|
51
|
-
import { useOpenApi } from '@qualisero/openapi-endpoint'
|
|
71
|
+
import { createApiClient } from '@qualisero/openapi-endpoint'
|
|
52
72
|
import axios from 'axios'
|
|
53
73
|
|
|
54
|
-
|
|
55
|
-
import {
|
|
56
|
-
QueryOperationId,
|
|
57
|
-
MutationOperationId,
|
|
58
|
-
openApiOperations,
|
|
59
|
-
type OpenApiOperations,
|
|
60
|
-
} from './generated/api-operations'
|
|
61
|
-
|
|
62
|
-
// Create axios instance
|
|
63
|
-
const axiosInstance = axios.create({
|
|
64
|
-
baseURL: 'https://api.example.com',
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
// Initialize the package with the auto-generated operations
|
|
68
|
-
const api = useOpenApi<OpenApiOperations>({
|
|
69
|
-
operations: openApiOperations,
|
|
70
|
-
axios: axiosInstance,
|
|
71
|
-
})
|
|
72
|
-
|
|
73
|
-
// Export for use in other parts of your application
|
|
74
|
-
export { api, QueryOperationId, MutationOperationId, OperationId }
|
|
74
|
+
const api = createApiClient(axiosInstance, queryClient?)
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
-
###
|
|
77
|
+
### Queries (GET/HEAD/OPTIONS)
|
|
78
78
|
|
|
79
79
|
```typescript
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
// Use queries for GET operations
|
|
84
|
-
const { data: pets, isLoading } = api.useQuery(QueryOperationId.listPets)
|
|
85
|
-
const { data: pet } = api.useQuery(QueryOperationId.getPet, { petId: '123' })
|
|
80
|
+
// No parameters
|
|
81
|
+
const { data, isLoading, error, refetch } = api.listPets.useQuery()
|
|
86
82
|
|
|
87
|
-
//
|
|
88
|
-
const
|
|
83
|
+
// With path parameters
|
|
84
|
+
const { data } = api.getPet.useQuery({ petId: '123' })
|
|
89
85
|
|
|
90
|
-
//
|
|
91
|
-
|
|
92
|
-
|
|
86
|
+
// With query parameters
|
|
87
|
+
const { data } = api.listPets.useQuery({
|
|
88
|
+
queryParams: { limit: 10, status: 'available' },
|
|
93
89
|
})
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
## Advanced Usage
|
|
97
|
-
|
|
98
|
-
### Reactive Query Parameters
|
|
99
|
-
|
|
100
|
-
The library supports type-safe, reactive query parameters that automatically trigger refetches when their values change:
|
|
101
|
-
|
|
102
|
-
```typescript
|
|
103
|
-
import { ref, computed } from 'vue'
|
|
104
|
-
import { api, QueryOperationId } from './api/init'
|
|
105
90
|
|
|
106
|
-
//
|
|
107
|
-
const { data: pets } = api.useQuery(QueryOperationId.listPets, {
|
|
108
|
-
queryParams: { limit: 10 },
|
|
109
|
-
})
|
|
110
|
-
// Results in: GET /pets?limit=10
|
|
111
|
-
|
|
112
|
-
// Reactive query parameters with computed
|
|
91
|
+
// Reactive parameters
|
|
113
92
|
const limit = ref(10)
|
|
114
|
-
const
|
|
93
|
+
const { data } = api.listPets.useQuery({
|
|
94
|
+
queryParams: computed(() => ({ limit: limit.value })),
|
|
95
|
+
})
|
|
96
|
+
// Automatically refetches when limit.value changes
|
|
115
97
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
98
|
+
// With options
|
|
99
|
+
const { data, onLoad } = api.listPets.useQuery({
|
|
100
|
+
enabled: computed(() => isLoggedIn.value),
|
|
101
|
+
staleTime: 5000,
|
|
102
|
+
onLoad: (data) => console.log('Loaded:', data),
|
|
121
103
|
})
|
|
122
104
|
|
|
123
|
-
//
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
// Query refetches with: GET /pets?limit=20&status=pending
|
|
127
|
-
|
|
128
|
-
// Combine with path parameters
|
|
129
|
-
const userPetsQuery = api.useQuery(
|
|
130
|
-
QueryOperationId.listUserPets,
|
|
131
|
-
computed(() => ({ userId: userId.value })),
|
|
132
|
-
{
|
|
133
|
-
queryParams: computed(() => ({
|
|
134
|
-
includeArchived: includeArchived.value,
|
|
135
|
-
})),
|
|
136
|
-
},
|
|
137
|
-
)
|
|
138
|
-
// Results in: GET /users/user-123/pets?includeArchived=false
|
|
105
|
+
// onLoad callback method
|
|
106
|
+
const query = api.getPet.useQuery({ petId: '123' })
|
|
107
|
+
query.onLoad((pet) => console.log('Pet:', pet.name))
|
|
139
108
|
```
|
|
140
109
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
- **Type-safe**: Query parameters are typed based on your OpenAPI specification
|
|
144
|
-
- **Reactive**: Supports `ref`, `computed`, and function-based values
|
|
145
|
-
- **Automatic refetch**: Changes to query params trigger automatic refetch via TanStack Query's key mechanism
|
|
146
|
-
- **Backward compatible**: Works alongside existing `axiosOptions.params`
|
|
147
|
-
|
|
148
|
-
### Automatic Cache Management and Refetching
|
|
149
|
-
|
|
150
|
-
By default, mutations automatically:
|
|
151
|
-
|
|
152
|
-
1. Update cache for matching queries with returned data
|
|
153
|
-
2. Invalidate them to trigger a reload
|
|
154
|
-
3. Invalidate matching list endpoints
|
|
110
|
+
### Mutations (POST/PUT/PATCH/DELETE)
|
|
155
111
|
|
|
156
112
|
```typescript
|
|
157
|
-
//
|
|
158
|
-
const createPet = api.useMutation(
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
//
|
|
162
|
-
const updatePet = api.useMutation(
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const
|
|
171
|
-
|
|
113
|
+
// Simple mutation
|
|
114
|
+
const createPet = api.createPet.useMutation()
|
|
115
|
+
await createPet.mutateAsync({ data: { name: 'Fluffy' } })
|
|
116
|
+
|
|
117
|
+
// With path parameters
|
|
118
|
+
const updatePet = api.updatePet.useMutation({ petId: '123' })
|
|
119
|
+
await updatePet.mutateAsync({ data: { name: 'Updated' } })
|
|
120
|
+
|
|
121
|
+
// Override path params at call time
|
|
122
|
+
const deletePet = api.deletePet.useMutation()
|
|
123
|
+
await deletePet.mutateAsync({ pathParams: { petId: '123' } })
|
|
124
|
+
|
|
125
|
+
// With options
|
|
126
|
+
const mutation = api.createPet.useMutation({
|
|
127
|
+
dontInvalidate: true,
|
|
128
|
+
invalidateOperations: ['listPets'],
|
|
129
|
+
onSuccess: (response) => console.log('Created:', response.data),
|
|
172
130
|
})
|
|
173
131
|
```
|
|
174
132
|
|
|
175
|
-
###
|
|
133
|
+
### Return Types
|
|
176
134
|
|
|
177
|
-
|
|
135
|
+
**Query Return:**
|
|
178
136
|
|
|
179
137
|
```typescript
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
data: formData,
|
|
189
|
-
axiosOptions: {
|
|
190
|
-
headers: {
|
|
191
|
-
'Content-Type': 'multipart/form-data',
|
|
192
|
-
},
|
|
193
|
-
},
|
|
194
|
-
})
|
|
138
|
+
{
|
|
139
|
+
data: ComputedRef<T | undefined>
|
|
140
|
+
isLoading: ComputedRef<boolean>
|
|
141
|
+
error: ComputedRef<Error | null>
|
|
142
|
+
isEnabled: ComputedRef<boolean>
|
|
143
|
+
queryKey: ComputedRef<unknown[]>
|
|
144
|
+
onLoad: (cb: (data: T) => void) => void
|
|
145
|
+
refetch: () => Promise<void>
|
|
195
146
|
}
|
|
147
|
+
```
|
|
196
148
|
|
|
197
|
-
|
|
198
|
-
async function uploadPetPictureAsString(petId: string, binaryData: string) {
|
|
199
|
-
const uploadMutation = api.useMutation(MutationOperationId.uploadPetPic, { petId })
|
|
149
|
+
**Mutation Return:**
|
|
200
150
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
async function handleFileUpload(event: Event, petId: string) {
|
|
210
|
-
const files = (event.target as HTMLInputElement).files
|
|
211
|
-
if (!files || files.length === 0) return
|
|
212
|
-
|
|
213
|
-
const file = files[0]
|
|
214
|
-
const formData = new FormData()
|
|
215
|
-
formData.append('file', file)
|
|
216
|
-
|
|
217
|
-
const uploadMutation = api.useMutation(
|
|
218
|
-
MutationOperationId.uploadPetPic,
|
|
219
|
-
{ petId },
|
|
220
|
-
{
|
|
221
|
-
invalidateOperations: [QueryOperationId.getPet, QueryOperationId.listPets],
|
|
222
|
-
onSuccess: (data) => {
|
|
223
|
-
console.log('Upload successful:', data)
|
|
224
|
-
},
|
|
225
|
-
onError: (error) => {
|
|
226
|
-
console.error('Upload failed:', error)
|
|
227
|
-
},
|
|
228
|
-
},
|
|
229
|
-
)
|
|
230
|
-
|
|
231
|
-
try {
|
|
232
|
-
await uploadMutation.mutateAsync({ data: formData })
|
|
233
|
-
} catch (error) {
|
|
234
|
-
console.error('Upload error:', error)
|
|
235
|
-
}
|
|
151
|
+
```typescript
|
|
152
|
+
{
|
|
153
|
+
data: ComputedRef<AxiosResponse<T> | undefined>
|
|
154
|
+
isPending: ComputedRef<boolean>
|
|
155
|
+
error: ComputedRef<Error | null>
|
|
156
|
+
mutate: (vars) => void
|
|
157
|
+
mutateAsync: (vars) => Promise<AxiosResponse>
|
|
158
|
+
extraPathParams: Ref<PathParams> // for dynamic path params
|
|
236
159
|
}
|
|
237
160
|
```
|
|
238
161
|
|
|
239
|
-
###
|
|
240
|
-
|
|
241
|
-
You can chain queries where one query provides the parameters for another:
|
|
162
|
+
### Type Helpers
|
|
242
163
|
|
|
243
164
|
```typescript
|
|
244
|
-
import {
|
|
245
|
-
|
|
246
|
-
//
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
//
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
// Query automatically enables/disables based on parameter availability
|
|
261
|
-
const petQuery = api.useQuery(
|
|
262
|
-
QueryOperationId.getPet,
|
|
263
|
-
computed(() => ({ petId: selectedPetId.value })),
|
|
264
|
-
)
|
|
265
|
-
|
|
266
|
-
// Query is automatically disabled when petId is null/undefined
|
|
267
|
-
console.log(petQuery.isEnabled.value) // false when selectedPetId.value is null
|
|
268
|
-
|
|
269
|
-
// Enable the query by setting the parameter
|
|
270
|
-
selectedPetId.value = '123'
|
|
271
|
-
console.log(petQuery.isEnabled.value) // true when selectedPetId.value is set
|
|
272
|
-
|
|
273
|
-
// Complex conditional enabling
|
|
274
|
-
const userId = ref<string>('user1')
|
|
275
|
-
const shouldFetchPets = ref(true)
|
|
276
|
-
|
|
277
|
-
const userPetsQuery = api.useQuery(
|
|
278
|
-
QueryOperationId.listUserPets,
|
|
279
|
-
computed(() => ({ userId: userId.value })),
|
|
280
|
-
{
|
|
281
|
-
enabled: computed(
|
|
282
|
-
() => shouldFetchPets.value, // Additional business logic
|
|
283
|
-
),
|
|
284
|
-
},
|
|
285
|
-
)
|
|
165
|
+
import type {
|
|
166
|
+
ApiResponse, // Response type (all fields required)
|
|
167
|
+
ApiResponseSafe, // Response with optional fields
|
|
168
|
+
ApiRequest, // Request body type
|
|
169
|
+
ApiPathParams, // Path parameters type
|
|
170
|
+
ApiQueryParams, // Query parameters type
|
|
171
|
+
} from './generated/api-operations'
|
|
172
|
+
|
|
173
|
+
// ApiResponse - ALL fields required
|
|
174
|
+
type PetResponse = ApiResponse<OpType.getPet>
|
|
175
|
+
// { readonly id: string, name: string, tag: string, status: 'available' | ... }
|
|
176
|
+
|
|
177
|
+
// ApiResponseSafe - only readonly required, others optional
|
|
178
|
+
type PetResponseSafe = ApiResponseSafe<OpType.getPet>
|
|
179
|
+
// { readonly id: string, name: string, tag?: string, status?: 'available' | ... }
|
|
286
180
|
```
|
|
287
181
|
|
|
288
|
-
###
|
|
182
|
+
### Enums
|
|
183
|
+
|
|
184
|
+
The CLI generates type-safe enum constants:
|
|
289
185
|
|
|
290
186
|
```typescript
|
|
291
|
-
import {
|
|
187
|
+
import { PetStatus } from './generated/api-enums'
|
|
292
188
|
|
|
293
|
-
// Use
|
|
294
|
-
const
|
|
295
|
-
|
|
296
|
-
queryParams: { limit: limit.value },
|
|
189
|
+
// Use enum for intellisense and typo safety
|
|
190
|
+
const { data } = api.listPets.useQuery({
|
|
191
|
+
queryParams: { status: PetStatus.Available },
|
|
297
192
|
})
|
|
298
193
|
|
|
299
|
-
//
|
|
300
|
-
|
|
301
|
-
|
|
194
|
+
// Still works with string literals
|
|
195
|
+
const { data } = api.listPets.useQuery({
|
|
196
|
+
queryParams: { status: 'available' }, // also valid
|
|
197
|
+
})
|
|
302
198
|
```
|
|
303
199
|
|
|
304
|
-
##
|
|
305
|
-
|
|
306
|
-
Full 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.
|
|
200
|
+
## Documentation
|
|
307
201
|
|
|
308
|
-
|
|
202
|
+
For detailed guides, see [docs/manual/](docs/manual/):
|
|
309
203
|
|
|
310
|
-
|
|
204
|
+
- [Getting Started](docs/manual/01-getting-started.md)
|
|
205
|
+
- [Queries](docs/manual/02-queries.md)
|
|
206
|
+
- [Mutations](docs/manual/03-mutations.md)
|
|
207
|
+
- [Reactive Parameters](docs/manual/04-reactive-parameters.md)
|
|
208
|
+
- [File Uploads](docs/manual/05-file-uploads.md)
|
|
209
|
+
- [Cache Management](docs/manual/06-cache-management.md)
|
|
311
210
|
|
|
312
211
|
## License
|
|
313
212
|
|