From 9404af441f833d16c1f31d2d163cab0169da8edf Mon Sep 17 00:00:00 2001 From: Shodai Suzuki Date: Thu, 20 Jun 2024 20:25:36 +0900 Subject: [PATCH] feat(fetch): include status code in `fetch` client response (#1470) * feat(fetch): include http status code in `fetch` response * chore: rerun `orval` in next sample app * chore: update return object in custom fetch function * chore: display http status code in next sample app --- packages/fetch/src/index.ts | 20 +++++++++-- .../next-app-with-fetch/app/gen/pets/pets.ts | 36 ++++++++++++++----- samples/next-app-with-fetch/app/pets.tsx | 5 ++- samples/next-app-with-fetch/custom-fetch.ts | 2 +- 4 files changed, 50 insertions(+), 13 deletions(-) diff --git a/packages/fetch/src/index.ts b/packages/fetch/src/index.ts index b26503aa5..1af02d0e2 100644 --- a/packages/fetch/src/index.ts +++ b/packages/fetch/src/index.ts @@ -62,6 +62,13 @@ ${ return \`${route}${queryParams ? '?${normalizedParams.toString()}' : ''}\` }\n`; + + const responseTypeName = `${operationName}Response`; + const responseTypeImplementation = `export type ${operationName}Response = { + data: ${response.definition.success || 'unknown'}; + status: number; +}`; + const getUrlFnProperties = props .filter( (prop) => @@ -79,7 +86,7 @@ ${ .join(','); const args = `${toObjectString(props, 'implementation')} ${isRequestOptions ? `options?: RequestInit` : ''}`; - const retrunType = `Promise<${response.definition.success || 'unknown'}>`; + const retrunType = `Promise<${responseTypeName}>`; const globalFetchOptions = isObject(override?.requestOptions) ? `${stringify(override?.requestOptions)?.slice(1, -1)?.trim()}` @@ -102,7 +109,12 @@ ${ ${fetchBodyOption} } `; - const fetchResponseImplementation = `const res = await fetch(${fetchFnOptions})\n\nreturn res.json()`; + const fetchResponseImplementation = `const res = await fetch(${fetchFnOptions} + ) + const data = await res.json() + + return { status: res.status, data } +`; const customFetchResponseImplementation = `return ${mutator?.name}<${retrunType}>(${fetchFnOptions});`; const bodyForm = generateFormDataAndUrlEncodedFunction({ @@ -120,7 +132,9 @@ ${ const fetchImplementation = `export const ${operationName} = async (${args}): ${retrunType} => {\n${fetchImplementationBody}}`; const implementation = - `${getUrlFnImplementation}\n` + `${fetchImplementation}\n`; + `${responseTypeImplementation}\n\n` + + `${getUrlFnImplementation}\n` + + `${fetchImplementation}\n`; return implementation; }; diff --git a/samples/next-app-with-fetch/app/gen/pets/pets.ts b/samples/next-app-with-fetch/app/gen/pets/pets.ts index 0d192d8d2..efcb09a7c 100644 --- a/samples/next-app-with-fetch/app/gen/pets/pets.ts +++ b/samples/next-app-with-fetch/app/gen/pets/pets.ts @@ -43,6 +43,11 @@ type NonReadonly = [T] extends [UnionToIntersection] /** * @summary List all pets */ +export type listPetsResponse = { + data: Pets; + status: number; +}; + export const getListPetsUrl = (params?: ListPetsParams) => { const normalizedParams = new URLSearchParams(); @@ -60,8 +65,8 @@ export const getListPetsUrl = (params?: ListPetsParams) => { export const listPets = async ( params?: ListPetsParams, options?: RequestInit, -): Promise => { - return customFetch>(getListPetsUrl(params), { +): Promise => { + return customFetch>(getListPetsUrl(params), { ...options, method: 'GET', }); @@ -70,6 +75,11 @@ export const listPets = async ( /** * @summary Create a pet */ +export type createPetsResponse = { + data: Pet; + status: number; +}; + export const getCreatePetsUrl = () => { return `http://localhost:3000/pets`; }; @@ -77,8 +87,8 @@ export const getCreatePetsUrl = () => { export const createPets = async ( createPetsBodyItem: CreatePetsBodyItem[], options?: RequestInit, -): Promise => { - return customFetch>(getCreatePetsUrl(), { +): Promise => { + return customFetch>(getCreatePetsUrl(), { ...options, method: 'POST', body: JSON.stringify(createPetsBodyItem), @@ -88,6 +98,11 @@ export const createPets = async ( /** * @summary Update a pet */ +export type updatePetsResponse = { + data: Pet; + status: number; +}; + export const getUpdatePetsUrl = () => { return `http://localhost:3000/pets`; }; @@ -95,8 +110,8 @@ export const getUpdatePetsUrl = () => { export const updatePets = async ( pet: NonReadonly, options?: RequestInit, -): Promise => { - return customFetch>(getUpdatePetsUrl(), { +): Promise => { + return customFetch>(getUpdatePetsUrl(), { ...options, method: 'PUT', body: JSON.stringify(pet), @@ -106,6 +121,11 @@ export const updatePets = async ( /** * @summary Info for a specific pet */ +export type showPetByIdResponse = { + data: Pet; + status: number; +}; + export const getShowPetByIdUrl = (petId: string) => { return `http://localhost:3000/pets/${petId}`; }; @@ -113,8 +133,8 @@ export const getShowPetByIdUrl = (petId: string) => { export const showPetById = async ( petId: string, options?: RequestInit, -): Promise => { - return customFetch>(getShowPetByIdUrl(petId), { +): Promise => { + return customFetch>(getShowPetByIdUrl(petId), { ...options, method: 'GET', }); diff --git a/samples/next-app-with-fetch/app/pets.tsx b/samples/next-app-with-fetch/app/pets.tsx index 646a92932..69d6f53b6 100644 --- a/samples/next-app-with-fetch/app/pets.tsx +++ b/samples/next-app-with-fetch/app/pets.tsx @@ -1,16 +1,19 @@ import { listPets } from './gen/pets/pets'; export default async function Pets() { - const pets = await listPets(); + const { data: pets, status } = await listPets(); return (

Pets by server actions

+
    {pets.map((pet) => (
  • {pet.name}
  • ))}
+ +

Status: {status}

); } diff --git a/samples/next-app-with-fetch/custom-fetch.ts b/samples/next-app-with-fetch/custom-fetch.ts index c39851bdb..1750f435e 100644 --- a/samples/next-app-with-fetch/custom-fetch.ts +++ b/samples/next-app-with-fetch/custom-fetch.ts @@ -53,5 +53,5 @@ export const customFetch = async ( const response = await fetch(request); const data = await getBody(response); - return data; + return { status: response.status, data } as T; };