fork of hey-api/openapi-ts because I need some additional things
1<script lang="ts" setup>
2import type { Pet } from '@/client'
3import { createClient } from '@/client/client'
4import { PetSchema } from '@/client/schemas.gen'
5import {
6 addPetMutation,
7 getPetByIdOptions,
8 updatePetMutation
9} from '@/client/@tanstack/vue-query.gen'
10import { useMutation, useQuery, useQueryClient } from '@tanstack/vue-query'
11import { computed, ref, watch } from 'vue'
12import type { RequestOptions } from '@/client/client'
13
14const queryClient = useQueryClient()
15
16const localClient = createClient({
17 // set default base url for requests made by this client
18 baseUrl: 'https://petstore3.swagger.io/api/v3',
19 /**
20 * Set default headers only for requests made by this client. This is to
21 * demonstrate local clients and their configuration taking precedence over
22 * internal service client.
23 */
24 headers: {
25 Authorization: 'Bearer <token_from_local_client>'
26 }
27})
28
29localClient.interceptors.request.use((request: Request, options: RequestOptions) => {
30 // Middleware is great for adding authorization tokens to requests made to
31 // protected paths. Headers are set randomly here to allow surfacing the
32 // default headers, too.
33 if (options.url === '/pet/{petId}' && options.method === 'GET' && Math.random() < 0.5) {
34 request.headers.set('Authorization', 'Bearer <token_from_interceptor>')
35 }
36 return request
37})
38
39const pet = ref<Pet>()
40const petId = ref<number>()
41
42const petInput = ref({ name: '', category: '' })
43
44const addPet = useMutation({
45 ...addPetMutation(),
46 onError: (error) => {
47 console.log(error)
48 },
49 onSuccess: (data) => {
50 pet.value = data
51 petId.value = data.id ?? petId.value
52
53 if (typeof data?.id === 'number') {
54 queryClient.invalidateQueries({
55 queryKey: getPetByIdOptions({ path: { petId: data.id } }).queryKey
56 })
57 }
58 }
59})
60
61const updatePet = useMutation({
62 ...updatePetMutation(),
63 onError: (error) => {
64 console.log(error)
65 },
66 onSuccess: (data) => {
67 pet.value = data
68 petId.value = data.id ?? petId.value
69
70 if (typeof data?.id === 'number') {
71 queryClient.invalidateQueries({
72 queryKey: getPetByIdOptions({ path: { petId: data.id } }).queryKey
73 })
74 }
75 }
76})
77
78const { data, error } = useQuery(
79 computed(() => ({
80 ...getPetByIdOptions({
81 client: localClient,
82 path: {
83 petId: petId.value!
84 }
85 }),
86 enabled: Boolean(petId.value)
87 }))
88)
89
90const handleAddPet = async () => {
91 if (PetSchema.required.includes('name') && !petInput.value?.name?.length) {
92 return
93 }
94
95 addPet.mutate({
96 body: {
97 category: {
98 id: 0,
99 name: petInput.value.category
100 },
101 id: 0,
102 name: petInput.value?.name,
103 photoUrls: ['string'],
104 status: 'available',
105 tags: [
106 {
107 id: 0,
108 name: 'string'
109 }
110 ]
111 }
112 })
113}
114
115function setRandomPetId() {
116 // random id 1-10
117 petId.value = Math.floor(Math.random() * (10 - 1 + 1) + 1)
118}
119
120const handleUpdatePet = async () => {
121 updatePet.mutate({
122 body: {
123 category: {
124 id: pet.value?.category?.id ?? 0,
125 name: petInput.value.category
126 },
127 id: pet.value?.id ?? 0,
128 name: petInput.value.name,
129 photoUrls: ['string'],
130 status: 'available',
131 tags: [
132 {
133 id: 0,
134 name: 'string'
135 }
136 ]
137 },
138 // setting headers per request
139 headers: {
140 Authorization: 'Bearer <token_from_method>'
141 }
142 })
143}
144
145watch(data, () => {
146 pet.value = data.value
147})
148
149watch(error, (error) => {
150 console.log(error)
151})
152</script>
153
154<template>
155 <div class="bg-[#18191b] py-12">
156 <div class="mx-auto flex max-w-md flex-col gap-12">
157 <div class="flex items-center">
158 <a class="shrink-0" href="https://heyapi.dev/" target="_blank">
159 <img
160 alt="Hey API logo"
161 class="size-16 transition duration-300 will-change-auto"
162 src="https://heyapi.dev/assets/raw/logo.png"
163 />
164 </a>
165
166 <h1 class="text-2xl font-bold text-white">@hey-api/openapi-ts 🤝 TanStack Vue Query</h1>
167 </div>
168
169 <div class="flex flex-col gap-2">
170 <div
171 class="flex max-w-60 items-center gap-3 rounded border border-[#575e64] bg-[#1f2123] p-4"
172 >
173 <div
174 class="flex size-10 place-content-center place-items-center rounded-full bg-[#233057] text-lg font-medium text-[#9eb1ff]"
175 >
176 <span>
177 {{ pet?.name?.slice(0, 1) || 'N' }}
178 </span>
179 </div>
180
181 <div>
182 <p class="text-sm font-bold text-white">Name: {{ pet?.name || 'N/A' }}</p>
183
184 <p class="text-sm text-[#f1f7feb5]">Category: {{ pet?.category?.name || 'N/A' }}</p>
185 </div>
186 </div>
187
188 <button
189 class="rounded bg-[#3e63dd] p-1 text-sm font-medium text-white"
190 type="button"
191 @click="setRandomPetId"
192 >
193 Get Random Pet
194 </button>
195 </div>
196
197 <form class="flex flex-col gap-3" @submit.prevent="handleAddPet">
198 <div class="flex w-64 flex-col gap-1">
199 <label class="font-medium text-white" for="name">Name</label>
200
201 <input
202 v-model="petInput.name"
203 class="rounded border border-[#575e64] bg-[#121314] p-1 text-sm text-white placeholder:text-[#575e64]"
204 name="name"
205 placeholder="Kitty"
206 required
207 />
208 </div>
209
210 <div class="flex w-64 flex-col gap-1">
211 <label class="font-medium text-white" for="category">Category</label>
212
213 <input
214 v-model="petInput.category"
215 class="rounded border border-[#575e64] bg-[#121314] p-1 text-sm text-white placeholder:text-[#575e64]"
216 name="category"
217 placeholder="Cats"
218 required
219 />
220 </div>
221
222 <div class="flex gap-2">
223 <button class="rounded bg-[#3e63dd] p-2 text-sm font-medium text-white" type="submit">
224 Add Pet
225 </button>
226
227 <button
228 class="rounded bg-[#3e63dd] p-2 text-sm font-medium text-white disabled:cursor-not-allowed"
229 :disabled="!pet"
230 type="button"
231 @click="handleUpdatePet"
232 >
233 Update Pet
234 </button>
235 </div>
236 </form>
237 </div>
238 </div>
239</template>