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