Skip to content

Commit

Permalink
feat: expose FetchError error type
Browse files Browse the repository at this point in the history
  • Loading branch information
johannschopplich committed Aug 24, 2023
1 parent 09b7a79 commit 7361355
Show file tree
Hide file tree
Showing 7 changed files with 38 additions and 34 deletions.
12 changes: 6 additions & 6 deletions playground/pages/jsonPlaceholder.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import type { FetchError } from 'ofetch'
import type { JsonPlaceholderComment } from '../types'
import type { FetchError } from '#nuxt-api-party'
const route = useRoute()
Expand Down Expand Up @@ -38,7 +38,7 @@ const formResponse = ref()
// Intended for similar use cases as `$fetch`
async function onSubmit() {
try {
formResponse.value = await $jsonPlaceholder('posts', {
formResponse.value = await $jsonPlaceholder('possssts', {
method: 'POST',
body: {
title: 'foo',
Expand All @@ -50,10 +50,10 @@ async function onSubmit() {
// eslint-disable-next-line no-console
console.log('formResponse:', formResponse.value)
}
catch (e) {
console.error('statusCode:', (e as FetchError).statusCode)
console.error('statusMessage:', (e as FetchError).statusMessage)
console.error('data:', (e as FetchError).data)
catch (error) {
console.error(error as FetchError)
// Log the API response body
console.error('Error response body:', (error as FetchError).data)
}
}
</script>
Expand Down
18 changes: 7 additions & 11 deletions playground/pages/petStore.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script setup lang="ts">
import { FetchError } from 'ofetch'
import type { components } from '#nuxt-api-party/petStore'
import type { FetchError } from '#nuxt-api-party'
type Pet = components['schemas']['Pet']
Expand All @@ -13,7 +13,7 @@ const { data: user, execute } = usePetStoreData('user/{username}', {
async function updateUser() {
try {
// will error because of authentication
// Will error because of authentication
await $petStore('user/{username}', {
method: 'put',
pathParams: { username: 'user1' },
Expand Down Expand Up @@ -73,15 +73,11 @@ async function abandonGarfield() {
} satisfies Pet,
})
}
catch (e) {
if (e instanceof FetchError) {
console.error('statusCode:', e.statusCode)
console.error('statusMessage:', e.statusMessage)
console.error('data:', e.data)
}
else {
console.error(e)
}
catch (error) {
const _error = error as FetchError
console.error(error as FetchError)
// Log the API response body
console.error('Error response body:', (error as FetchError).data)
}
}
</script>
Expand Down
1 change: 1 addition & 0 deletions src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ import type { UseApiData, UseOpenApiData, UseApiDataOptions } from '${relativeTo
${schemaEndpointIds.map(i => `import type { paths as ${pascalCase(i)}Paths } from '#${moduleName}/${i}'`).join('')}
export type { FetchError } from '${relativeTo('runtime/types')}'
export type { $Api, $OpenApi, ApiFetchOptions, UseApiData, UseOpenApiData, UseApiDataOptions }
${endpointKeys.map(i => `
Expand Down
3 changes: 1 addition & 2 deletions src/runtime/composables/useApiData.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import { computed, reactive } from 'vue'
import { hash } from 'ohash'
import type { FetchError } from 'ofetch'
import type { NitroFetchOptions } from 'nitropack'
import type { WatchSource } from 'vue'
import type { AsyncData, AsyncDataOptions } from 'nuxt/app'
import type { ModuleOptions } from '../../module'
import { headersToObject, resolvePath, serializeMaybeEncodedBody, toValue } from '../utils'
import { isFormData } from '../formData'
import type { EndpointFetchOptions, MaybeRef, MaybeRefOrGetter } from '../utils'
import type { AllPaths, GETPaths, GETPlainPaths, HttpMethod, IgnoreCase, OpenApiError, OpenApiRequestOptions, OpenApiResponse, PathItemObject } from '../types'
import type { AllPaths, FetchError, GETPaths, GETPlainPaths, HttpMethod, IgnoreCase, OpenApiError, OpenApiRequestOptions, OpenApiResponse, PathItemObject } from '../types'
import { useAsyncData, useRequestHeaders, useRuntimeConfig } from '#imports'

type ComputedOptions<T extends Record<string, any>> = {
Expand Down
14 changes: 10 additions & 4 deletions src/runtime/server/handler.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { createError, defineEventHandler, getRequestHeader, getRouterParam, readBody } from 'h3'
import type { FetchError } from 'ofetch'
import type { ModuleOptions } from '../../module'
import { deserializeMaybeEncodedBody } from '../utils'
import type { ModuleOptions } from '../../module'
import type { EndpointFetchOptions } from '../utils'
import type { FetchError } from '../types'
import { useRuntimeConfig } from '#imports'

export default defineEventHandler(async (event): Promise<any> => {
Expand Down Expand Up @@ -71,7 +71,13 @@ export default defineEventHandler(async (event): Promise<any> => {
},
)
}
catch (err) {
throw createError((err as FetchError))
catch (error) {
const { response } = error as FetchError

throw createError({
statusCode: response?.status,
statusMessage: response?.statusText,
data: response?._data,
})
}
})
22 changes: 12 additions & 10 deletions src/runtime/types.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,18 @@
import type { IFetchError, FetchError as _FetchError } from 'ofetch'
import type { NitroFetchOptions } from 'nitropack'

// --- General purpose types ---

export type FetchError = _FetchError & IFetchError

// --- OpenAPI types ---

export type IgnoreCase<T extends string> = Lowercase<T> | Uppercase<T>
export type RemovePrefix<T extends string, P extends string> = T extends `${P}${infer S}` ? S : never

export type PathItemObject = { [M in HttpMethod]?: any } & { parameters?: any }

// Constant types
// HTTP status codes and methods
export type HttpMethod = 'get' | 'put' | 'post' | 'delete' | 'options' | 'head' | 'patch' | 'trace'
export type OkStatus = 200 | 201 | 202 | 203 | 204 | 206 | 207 | '2XX' | 'default'
export type ErrorStatus = 500 | '5XX' | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 429 | 431 | 444 | 450 | 451 | 497 | 498 | 499 | '4XX'
Expand Down Expand Up @@ -48,7 +55,7 @@ export type OpenApiRequestOptions<
M extends IgnoreCase<keyof P & HttpMethod> = IgnoreCase<keyof P & 'get'>,
> = Omit<
NitroFetchOptions<any, Lowercase<M>>,
'params' | 'query' | 'headers' | 'body' | 'method'
'params' | 'query' | 'headers' | 'method' | 'body'
> & RequestBody<P[Lowercase<M>]> & PathParameters<P[Lowercase<M>]> & QueryParameters<P[Lowercase<M>]> & Method<M>

type MediaTypes<T, Status extends keyof any> = {
Expand All @@ -69,19 +76,14 @@ type MediaTypes<T, Status extends keyof any> = {
export type OpenApiResponse<T> = MediaTypes<T, OkStatus>
export type OpenApiError<T> = MediaTypes<T, ErrorStatus>

export type AllPaths<Paths> =
RemovePrefix<keyof Paths & string, '/'>
export type AllPaths<Paths> = RemovePrefix<keyof Paths & string, '/'>

/**
* All endpoints that don't require a `method` property
*/
/** All endpoints that don't require a `method` property */
export type GETPaths<Paths> = {
[P in keyof Paths]: Paths[P] extends { get: any } ? RemovePrefix<P & string, '/'> : never;
}[keyof Paths]

/**
* All endpoints that don't require additional options
*/
/** All endpoints that don't require additional options */
export type GETPlainPaths<Paths> = {
[P in keyof Paths]: Paths[P] extends { get: infer O }
? O extends { parameters: { query: any } | { header: any } | { path: any } }
Expand Down
2 changes: 1 addition & 1 deletion test/fixture/pages/invalid.vue
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<script setup lang="ts">
import type { FetchError } from 'ofetch'
import type { FetchError } from '#nuxt-api-party'
try {
await $testApi('not-found', {
Expand Down

0 comments on commit 7361355

Please sign in to comment.