diff --git a/.changeset/propagate-empty-error-response.md b/.changeset/propagate-empty-error-response.md new file mode 100644 index 000000000..81e04a621 --- /dev/null +++ b/.changeset/propagate-empty-error-response.md @@ -0,0 +1,5 @@ +--- +"openapi-react-query": patch +--- + +fix(openapi-react-query): propagate undefined errors from empty-body non-OK responses diff --git a/packages/openapi-react-query/src/index.ts b/packages/openapi-react-query/src/index.ts index fb9683164..acd6a5f49 100644 --- a/packages/openapi-react-query/src/index.ts +++ b/packages/openapi-react-query/src/index.ts @@ -200,8 +200,8 @@ export default function createClient; const fn = client[mth] as ClientMethod; const { data, error, response } = await fn(path, { signal, ...(init as any) }); // TODO: find a way to avoid as any - if (error) { - throw error; + if (error !== undefined || !response.ok) { + throw error ?? new Error(`Request failed with status ${response.status}`); } if (response.status === 204 || response.headers.get("Content-Length") === "0") { return data ?? null; @@ -247,9 +247,9 @@ export default function createClient { const mth = method.toUpperCase() as Uppercase; const fn = client[mth] as ClientMethod; - const { data, error } = await fn(path, init as InitWithUnknowns); - if (error) { - throw error; + const { data, error, response } = await fn(path, init as InitWithUnknowns); + if (error !== undefined || !response.ok) { + throw error ?? new Error(`Request failed with status ${response.status}`); } return data as Exclude; diff --git a/packages/openapi-react-query/test/index.test.tsx b/packages/openapi-react-query/test/index.test.tsx index 78715e643..ce4f25dbf 100644 --- a/packages/openapi-react-query/test/index.test.tsx +++ b/packages/openapi-react-query/test/index.test.tsx @@ -430,6 +430,26 @@ describe("client", () => { expect(data).toBeNull(); }); + it("should propagate undefined errors from empty non-OK responses", async () => { + const fetchClient = createFetchClient({ baseUrl }); + const client = createClient(fetchClient); + + useMockRequestHandler({ + baseUrl, + method: "get", + path: "/string-array", + status: 500, + headers: { + "Content-Length": "0", + }, + body: undefined, + }); + + await expect(queryClient.fetchQuery(client.queryOptions("get", "/string-array"))).rejects.toThrow( + "Request failed with status 500", + ); + }); + it("should infer correct data and error type", async () => { const fetchClient = createFetchClient({ baseUrl, fetch: fetchInfinite }); const client = createClient(fetchClient); @@ -902,6 +922,30 @@ describe("client", () => { await expect(result.current.mutateAsync({ body: { message: "Hello", replied_at: 0 } })).rejects.toThrow(); }); + it("should propagate undefined errors from empty non-OK responses", async () => { + const fetchClient = createFetchClient({ baseUrl }); + const client = createClient(fetchClient); + + useMockRequestHandler({ + baseUrl, + method: "put", + path: "/comment", + status: 500, + headers: { + "Content-Length": "0", + }, + body: undefined, + }); + + const { result } = renderHook(() => client.useMutation("put", "/comment"), { + wrapper, + }); + + await expect( + result.current.mutateAsync({ body: { message: "Hello", replied_at: 0 } }), + ).rejects.toThrow("Request failed with status 500"); + }); + it("should use provided custom queryClient", async () => { const fetchClient = createFetchClient({ baseUrl }); const client = createClient(fetchClient); @@ -1268,5 +1312,47 @@ describe("client", () => { expect(result.current.data).toEqual([1, 2, 3, 4, 5, 6]); }); + + it("should propagate undefined errors from empty non-OK responses", async () => { + const fetchClient = createFetchClient({ baseUrl }); + const client = createClient(fetchClient); + + useMockRequestHandler({ + baseUrl, + method: "get", + path: "/paginated-data", + status: 500, + headers: { + "Content-Length": "0", + }, + body: undefined, + }); + + const { result } = renderHook( + () => + client.useInfiniteQuery( + "get", + "/paginated-data", + { + params: { + query: { + limit: 3, + }, + }, + }, + { + getNextPageParam: (lastPage) => lastPage.nextPage, + initialPageParam: 0, + }, + ), + { wrapper }, + ); + + await waitFor(() => expect(result.current.isError).toBe(true)); + + expect(result.current.error).toBeInstanceOf(Error); + expect((result.current.error as Error).message).toBe("Request failed with status 500"); + expect(result.current.data).toBeUndefined(); + }); }); });