Skip to content
  •  
  •  
  •  
20 changes: 20 additions & 0 deletions .changeset/client-unwrap-method.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
'@kubb/plugin-axios': minor
'@kubb/plugin-fetch': minor
'@kubb/plugin-react-query': patch
'@kubb/plugin-vue-query': patch
'@kubb/plugin-swr': patch
---

Generated calls now return a promise with an extra `unwrap()` method. Calling it gives you the bare
success body, or rejects with `error` when the call was made with `throwOnError: false`.

```ts
const { data, error } = await getPetById({ path: { petId: 1 } })
const pet = await getPetById({ path: { petId: 1 } }).unwrap()
```

Awaiting the call directly still gives the full result, so nothing existing changes.

`plugin-react-query`, `plugin-vue-query`, and `plugin-swr` now build their generated query and
mutation bodies on top of `unwrap()` too, instead of destructuring the result by hand.
26 changes: 26 additions & 0 deletions examples/advanced/src/gen/.kubb/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,32 @@ export type RequestResult<TResponses, ThrowOnError extends boolean = true, TRequ
? { status: number; data: undefined; error: undefined; contentType: string | undefined; request: TRequest; response: TResponse }
: ResultUnion<TResponses, TRequest, TResponse>

/**
* A `RequestResult` promise with an extra `unwrap()` method that resolves to the success body.
*/
export type Unwrappable<T extends { data: unknown; error: unknown }> = Promise<T> & {
unwrap: () => Promise<Extract<T, { error: undefined }>['data']>
}

/**
* Attaches `unwrap()` to a result promise, which rejects with `error` when the result carried one.
*
* @example Full result
* `const { data, error } = await getPetById({ path: { petId: 1 } })`
*
* @example Success body only
* `const pet = await getPetById({ path: { petId: 1 } }).unwrap()`
*/
export function withUnwrap<T extends { data: unknown; error: unknown }>(promise: Promise<T>): Unwrappable<T> {
const unwrappable = promise as Unwrappable<T>
unwrappable.unwrap = () =>
promise.then((result) => {
if (result.error !== undefined) throw result.error
return result.data as Extract<T, { error: undefined }>['data']
})
return unwrappable
}

/**
* The data-shaped keys of the grouped options object, which `Options` re-adds typed per operation.
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Options, RequestResult } from '../../../.kubb/client'
import type { Options, Unwrappable, RequestResult } from '../../../.kubb/client'
import type { AddFilesOptions, AddFilesResponses } from '../../../models/ts/pet/AddFiles'
import { client } from '../../../.kubb/client'
import { client, withUnwrap } from '../../../.kubb/client'

/**
* @description Place a new file in the store
Expand All @@ -9,8 +9,8 @@ import { client } from '../../../.kubb/client'
*/
export function addFiles<ThrowOnError extends boolean = true>(
options: Options<AddFilesOptions, ThrowOnError>,
): Promise<RequestResult<AddFilesResponses, ThrowOnError>> {
): Unwrappable<RequestResult<AddFilesResponses, ThrowOnError>> {
const { client: request = client, ...config } = options

return request({ method: 'POST', url: '/pet/files', ...config }) as Promise<RequestResult<AddFilesResponses, ThrowOnError>>
return withUnwrap(request({ method: 'POST', url: '/pet/files', ...config }) as Promise<RequestResult<AddFilesResponses, ThrowOnError>>)
}
22 changes: 12 additions & 10 deletions examples/advanced/src/gen/clients/axios/petService/addPet.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Options, RequestResult } from '../../../.kubb/client'
import type { Options, Unwrappable, RequestResult } from '../../../.kubb/client'
import type { AddPetOptions, AddPetResponses } from '../../../models/ts/pet/AddPet'
import { client } from '../../../.kubb/client'
import { client, withUnwrap } from '../../../.kubb/client'
import { addPetResponseSchema, addPetErrorSchema } from '../../../zod/pet/addPetSchema'

/**
Expand All @@ -10,14 +10,16 @@ import { addPetResponseSchema, addPetErrorSchema } from '../../../zod/pet/addPet
*/
export function addPet<ThrowOnError extends boolean = true>(
options: Options<AddPetOptions, ThrowOnError>,
): Promise<RequestResult<AddPetResponses, ThrowOnError>> {
): Unwrappable<RequestResult<AddPetResponses, ThrowOnError>> {
const { client: request = client, ...config } = options

return request({
method: 'POST',
url: '/pet',
security: [{ type: 'oauth2' }],
validator: { response: addPetResponseSchema, error: addPetErrorSchema },
...config,
}) as Promise<RequestResult<AddPetResponses, ThrowOnError>>
return withUnwrap(
request({
method: 'POST',
url: '/pet',
security: [{ type: 'oauth2' }],
validator: { response: addPetResponseSchema, error: addPetErrorSchema },
...config,
}) as Promise<RequestResult<AddPetResponses, ThrowOnError>>,
)
}
22 changes: 12 additions & 10 deletions examples/advanced/src/gen/clients/axios/petService/deletePet.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Options, RequestResult } from '../../../.kubb/client'
import type { Options, Unwrappable, RequestResult } from '../../../.kubb/client'
import type { DeletePetOptions, DeletePetResponses } from '../../../models/ts/pet/DeletePet'
import { client } from '../../../.kubb/client'
import { client, withUnwrap } from '../../../.kubb/client'
import { deletePetResponseSchema, deletePetErrorSchema } from '../../../zod/pet/deletePetSchema'

/**
Expand All @@ -10,14 +10,16 @@ import { deletePetResponseSchema, deletePetErrorSchema } from '../../../zod/pet/
*/
export function deletePet<ThrowOnError extends boolean = true>(
options: Options<DeletePetOptions, ThrowOnError>,
): Promise<RequestResult<DeletePetResponses, ThrowOnError>> {
): Unwrappable<RequestResult<DeletePetResponses, ThrowOnError>> {
const { client: request = client, ...config } = options

return request({
method: 'DELETE',
url: '/pet/{petId}:search',
security: [{ type: 'oauth2' }],
validator: { response: deletePetResponseSchema, error: deletePetErrorSchema },
...config,
}) as Promise<RequestResult<DeletePetResponses, ThrowOnError>>
return withUnwrap(
request({
method: 'DELETE',
url: '/pet/{petId}:search',
security: [{ type: 'oauth2' }],
validator: { response: deletePetResponseSchema, error: deletePetErrorSchema },
...config,
}) as Promise<RequestResult<DeletePetResponses, ThrowOnError>>,
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Options, RequestResult } from '../../../.kubb/client'
import type { Options, Unwrappable, RequestResult } from '../../../.kubb/client'
import type { FindPetsByStatusOptions, FindPetsByStatusResponses } from '../../../models/ts/pet/FindPetsByStatus'
import { client } from '../../../.kubb/client'
import { client, withUnwrap } from '../../../.kubb/client'
import { findPetsByStatusResponseSchema, findPetsByStatusErrorSchema } from '../../../zod/pet/findPetsByStatusSchema'

/**
Expand All @@ -10,14 +10,16 @@ import { findPetsByStatusResponseSchema, findPetsByStatusErrorSchema } from '../
*/
export function findPetsByStatus<ThrowOnError extends boolean = true>(
options: Options<FindPetsByStatusOptions, ThrowOnError>,
): Promise<RequestResult<FindPetsByStatusResponses, ThrowOnError>> {
): Unwrappable<RequestResult<FindPetsByStatusResponses, ThrowOnError>> {
const { client: request = client, ...config } = options

return request({
method: 'GET',
url: '/pet/findByStatus/{step_id}',
security: [{ type: 'oauth2' }],
validator: { response: findPetsByStatusResponseSchema, error: findPetsByStatusErrorSchema },
...config,
}) as Promise<RequestResult<FindPetsByStatusResponses, ThrowOnError>>
return withUnwrap(
request({
method: 'GET',
url: '/pet/findByStatus/{step_id}',
security: [{ type: 'oauth2' }],
validator: { response: findPetsByStatusResponseSchema, error: findPetsByStatusErrorSchema },
...config,
}) as Promise<RequestResult<FindPetsByStatusResponses, ThrowOnError>>,
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Options, RequestResult } from '../../../.kubb/client'
import type { Options, Unwrappable, RequestResult } from '../../../.kubb/client'
import type { FindPetsByTagsOptions, FindPetsByTagsResponses } from '../../../models/ts/pet/FindPetsByTags'
import { client } from '../../../.kubb/client'
import { client, withUnwrap } from '../../../.kubb/client'
import { findPetsByTagsResponseSchema, findPetsByTagsErrorSchema } from '../../../zod/pet/findPetsByTagsSchema'

/**
Expand All @@ -10,15 +10,17 @@ import { findPetsByTagsResponseSchema, findPetsByTagsErrorSchema } from '../../.
*/
export function findPetsByTags<ThrowOnError extends boolean = true>(
options: Options<FindPetsByTagsOptions, ThrowOnError>,
): Promise<RequestResult<FindPetsByTagsResponses, ThrowOnError>> {
): Unwrappable<RequestResult<FindPetsByTagsResponses, ThrowOnError>> {
const { client: request = client, ...config } = options

return request({
method: 'GET',
url: '/pet/findByTags',
security: [{ type: 'oauth2' }],
styles: { query: { tags: { explode: true } } },
validator: { response: findPetsByTagsResponseSchema, error: findPetsByTagsErrorSchema },
...config,
}) as Promise<RequestResult<FindPetsByTagsResponses, ThrowOnError>>
return withUnwrap(
request({
method: 'GET',
url: '/pet/findByTags',
security: [{ type: 'oauth2' }],
styles: { query: { tags: { explode: true } } },
validator: { response: findPetsByTagsResponseSchema, error: findPetsByTagsErrorSchema },
...config,
}) as Promise<RequestResult<FindPetsByTagsResponses, ThrowOnError>>,
)
}
22 changes: 12 additions & 10 deletions examples/advanced/src/gen/clients/axios/petService/getPetById.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Options, RequestResult } from '../../../.kubb/client'
import type { Options, Unwrappable, RequestResult } from '../../../.kubb/client'
import type { GetPetByIdOptions, GetPetByIdResponses } from '../../../models/ts/pet/GetPetById'
import { client } from '../../../.kubb/client'
import { client, withUnwrap } from '../../../.kubb/client'
import { getPetByIdResponseSchema, getPetByIdErrorSchema } from '../../../zod/pet/getPetByIdSchema'

/**
Expand All @@ -10,14 +10,16 @@ import { getPetByIdResponseSchema, getPetByIdErrorSchema } from '../../../zod/pe
*/
export function getPetById<ThrowOnError extends boolean = true>(
options: Options<GetPetByIdOptions, ThrowOnError>,
): Promise<RequestResult<GetPetByIdResponses, ThrowOnError>> {
): Unwrappable<RequestResult<GetPetByIdResponses, ThrowOnError>> {
const { client: request = client, ...config } = options

return request({
method: 'GET',
url: '/pet/{petId}:search',
security: [{ type: 'apiKey', name: 'api_key', in: 'header' }, { type: 'oauth2' }],
validator: { response: getPetByIdResponseSchema, error: getPetByIdErrorSchema },
...config,
}) as Promise<RequestResult<GetPetByIdResponses, ThrowOnError>>
return withUnwrap(
request({
method: 'GET',
url: '/pet/{petId}:search',
security: [{ type: 'apiKey', name: 'api_key', in: 'header' }, { type: 'oauth2' }],
validator: { response: getPetByIdResponseSchema, error: getPetByIdErrorSchema },
...config,
}) as Promise<RequestResult<GetPetByIdResponses, ThrowOnError>>,
)
}
22 changes: 12 additions & 10 deletions examples/advanced/src/gen/clients/axios/petService/updatePet.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Options, RequestResult } from '../../../.kubb/client'
import type { Options, Unwrappable, RequestResult } from '../../../.kubb/client'
import type { UpdatePetOptions, UpdatePetResponses } from '../../../models/ts/pet/UpdatePet'
import { client } from '../../../.kubb/client'
import { client, withUnwrap } from '../../../.kubb/client'
import { updatePetResponseSchema, updatePetErrorSchema } from '../../../zod/pet/updatePetSchema'

/**
Expand All @@ -10,14 +10,16 @@ import { updatePetResponseSchema, updatePetErrorSchema } from '../../../zod/pet/
*/
export function updatePet<ThrowOnError extends boolean = true>(
options: Options<UpdatePetOptions, ThrowOnError>,
): Promise<RequestResult<UpdatePetResponses, ThrowOnError>> {
): Unwrappable<RequestResult<UpdatePetResponses, ThrowOnError>> {
const { client: request = client, ...config } = options

return request({
method: 'PUT',
url: '/pet',
security: [{ type: 'oauth2' }],
validator: { response: updatePetResponseSchema, error: updatePetErrorSchema },
...config,
}) as Promise<RequestResult<UpdatePetResponses, ThrowOnError>>
return withUnwrap(
request({
method: 'PUT',
url: '/pet',
security: [{ type: 'oauth2' }],
validator: { response: updatePetResponseSchema, error: updatePetErrorSchema },
...config,
}) as Promise<RequestResult<UpdatePetResponses, ThrowOnError>>,
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Options, RequestResult } from '../../../.kubb/client'
import type { Options, Unwrappable, RequestResult } from '../../../.kubb/client'
import type { UpdatePetWithFormOptions, UpdatePetWithFormResponses } from '../../../models/ts/pet/UpdatePetWithForm'
import { client } from '../../../.kubb/client'
import { client, withUnwrap } from '../../../.kubb/client'
import { updatePetWithFormResponseSchema, updatePetWithFormErrorSchema } from '../../../zod/pet/updatePetWithFormSchema'

/**
Expand All @@ -9,14 +9,16 @@ import { updatePetWithFormResponseSchema, updatePetWithFormErrorSchema } from '.
*/
export function updatePetWithForm<ThrowOnError extends boolean = true>(
options: Options<UpdatePetWithFormOptions, ThrowOnError>,
): Promise<RequestResult<UpdatePetWithFormResponses, ThrowOnError>> {
): Unwrappable<RequestResult<UpdatePetWithFormResponses, ThrowOnError>> {
const { client: request = client, ...config } = options

return request({
method: 'POST',
url: '/pet/{petId}:search',
security: [{ type: 'oauth2' }],
validator: { response: updatePetWithFormResponseSchema, error: updatePetWithFormErrorSchema },
...config,
}) as Promise<RequestResult<UpdatePetWithFormResponses, ThrowOnError>>
return withUnwrap(
request({
method: 'POST',
url: '/pet/{petId}:search',
security: [{ type: 'oauth2' }],
validator: { response: updatePetWithFormResponseSchema, error: updatePetWithFormErrorSchema },
...config,
}) as Promise<RequestResult<UpdatePetWithFormResponses, ThrowOnError>>,
)
}
22 changes: 12 additions & 10 deletions examples/advanced/src/gen/clients/axios/petService/uploadFile.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
import type { Options, RequestResult } from '../../../.kubb/client'
import type { Options, Unwrappable, RequestResult } from '../../../.kubb/client'
import type { UploadFileOptions, UploadFileResponses } from '../../../models/ts/pet/UploadFile'
import { client } from '../../../.kubb/client'
import { client, withUnwrap } from '../../../.kubb/client'

/**
* @summary uploads an image
* {@link /pet/:petId/uploadImage}
*/
export function uploadFile<ThrowOnError extends boolean = true>(
options: Options<UploadFileOptions, ThrowOnError>,
): Promise<RequestResult<UploadFileResponses, ThrowOnError>> {
): Unwrappable<RequestResult<UploadFileResponses, ThrowOnError>> {
const { client: request = client, ...config } = options

return request({
method: 'POST',
url: '/pet/{petId}/uploadImage',
security: [{ type: 'oauth2' }],
contentType: { request: 'application/octet-stream' },
...config,
}) as Promise<RequestResult<UploadFileResponses, ThrowOnError>>
return withUnwrap(
request({
method: 'POST',
url: '/pet/{petId}/uploadImage',
security: [{ type: 'oauth2' }],
contentType: { request: 'application/octet-stream' },
...config,
}) as Promise<RequestResult<UploadFileResponses, ThrowOnError>>,
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { Options, RequestResult } from '../../../.kubb/client'
import type { Options, Unwrappable, RequestResult } from '../../../.kubb/client'
import type { CreatePetsOptions, CreatePetsResponses } from '../../../models/ts/pets/CreatePets'
import { client } from '../../../.kubb/client'
import { client, withUnwrap } from '../../../.kubb/client'
import { createPetsResponseSchema, createPetsErrorSchema } from '../../../zod/pets/createPetsSchema'

/**
Expand All @@ -9,13 +9,12 @@ import { createPetsResponseSchema, createPetsErrorSchema } from '../../../zod/pe
*/
export function createPets<ThrowOnError extends boolean = true>(
options: Options<CreatePetsOptions, ThrowOnError>,
): Promise<RequestResult<CreatePetsResponses, ThrowOnError>> {
): Unwrappable<RequestResult<CreatePetsResponses, ThrowOnError>> {
const { client: request = client, ...config } = options

return request({
method: 'POST',
url: '/pets/{uuid}',
validator: { response: createPetsResponseSchema, error: createPetsErrorSchema },
...config,
}) as Promise<RequestResult<CreatePetsResponses, ThrowOnError>>
return withUnwrap(
request({ method: 'POST', url: '/pets/{uuid}', validator: { response: createPetsResponseSchema, error: createPetsErrorSchema }, ...config }) as Promise<
RequestResult<CreatePetsResponses, ThrowOnError>
>,
)
}
3 changes: 1 addition & 2 deletions examples/advanced/src/gen/clients/hooks/pet/useAddFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ export function addFilesMutationOptions<TContext = unknown>(
return mutationOptions<AddFilesStatus200, ResponseErrorConfig<AddFilesStatus405>, AddFilesOptions, TContext>({
mutationKey,
mutationFn: async ({ body }) => {
const { data } = await addFiles({ ...config, body, throwOnError: true })
return data
return addFiles({ ...config, body, throwOnError: true }).unwrap()
},
})
}
Expand Down
3 changes: 1 addition & 2 deletions examples/advanced/src/gen/clients/hooks/pet/useAddPet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ export function addPetMutationOptions<TContext = unknown>(
return mutationOptions<AddPetResponse, ResponseErrorConfig<AddPetStatus405>, AddPetOptions, TContext>({
mutationKey,
mutationFn: async ({ body }) => {
const { data } = await addPet({ ...config, body, throwOnError: true })
return data
return addPet({ ...config, body, throwOnError: true }).unwrap()
},
})
}
Expand Down
Loading
Loading