From dd1ec7d0b4fd64b13a26249ee67a20d09881540e Mon Sep 17 00:00:00 2001 From: Tomas Rimkus Date: Tue, 23 Sep 2025 18:39:46 +0300 Subject: [PATCH 1/3] fix: "no content" response handling when the response type is "json" --- src/gaxios.ts | 8 +++++++- test/test.getch.ts | 19 +++++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/src/gaxios.ts b/src/gaxios.ts index a8ef4c7f..f253d2e6 100644 --- a/src/gaxios.ts +++ b/src/gaxios.ts @@ -44,6 +44,8 @@ interface FetchCompliance { fetch: typeof fetch; } +const HTTP_STATUS_NO_CONTENT = 204; + export class Gaxios implements FetchCompliance { protected agentCache = new Map< string | URL, @@ -254,6 +256,10 @@ export class Gaxios implements FetchCompliance { opts: GaxiosOptionsPrepared, res: Response, ): Promise> { + if (res.status === HTTP_STATUS_NO_CONTENT) { + return ''; + } + if ( opts.maxContentLength && res.headers.has('content-length') && @@ -605,7 +611,7 @@ export class Gaxios implements FetchCompliance { * This implementation follows the spec: https://www.ietf.org/rfc/rfc2387.txt. However, recursive * multipart/related requests are not currently supported. * - * @param {GaxioMultipartOptions[]} multipartOptions the pieces to turn into a multipart/related body. + * @param {GaxiosMultipartOptions[]} multipartOptions the pieces to turn into a multipart/related body. * @param {string} boundary the boundary string to be placed between each part. */ private async *getMultipartRequest( diff --git a/test/test.getch.ts b/test/test.getch.ts index b05cf101..d1961f4f 100644 --- a/test/test.getch.ts +++ b/test/test.getch.ts @@ -1214,6 +1214,25 @@ describe('🎏 data handling', () => { scope.done(); } }); + + it('should handle "204 No Content" responses when response type is "json"', async () => { + const scope = nock(url) + .matchHeader('content-type', 'application/json') + .put('/') + .reply(204, '', {'Content-Type': 'application/json'}); + const res = await request({ + url, + method: 'PUT', + data: {}, + headers: new Headers({ + 'content-type': 'application/json', + accept: 'application/json', + }), + responseType: 'json', + }); + scope.done(); + assert.deepStrictEqual(res.data, ''); + }); }); describe('🍂 defaults & instances', () => { From 783c1225e49c8a45f05a41936f37c765f89b9d43 Mon Sep 17 00:00:00 2001 From: Tomas Rimkus Date: Tue, 23 Sep 2025 18:39:46 +0300 Subject: [PATCH 2/3] fix: "no content" response handling when the response type is "json" --- src/gaxios.ts | 15 +++++++++++++-- test/test.getch.ts | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) diff --git a/src/gaxios.ts b/src/gaxios.ts index a8ef4c7f..cceeef87 100644 --- a/src/gaxios.ts +++ b/src/gaxios.ts @@ -44,6 +44,8 @@ interface FetchCompliance { fetch: typeof fetch; } +const HTTP_STATUS_NO_CONTENT = 204; + export class Gaxios implements FetchCompliance { protected agentCache = new Map< string | URL, @@ -254,6 +256,10 @@ export class Gaxios implements FetchCompliance { opts: GaxiosOptionsPrepared, res: Response, ): Promise> { + if (res.status === HTTP_STATUS_NO_CONTENT) { + return ''; + } + if ( opts.maxContentLength && res.headers.has('content-length') && @@ -271,7 +277,12 @@ export class Gaxios implements FetchCompliance { case 'stream': return res.body; case 'json': - return res.json(); + const data = await res.text(); + try { + return JSON.parse(data); + } catch { + return data; + } case 'arraybuffer': return res.arrayBuffer(); case 'blob': @@ -605,7 +616,7 @@ export class Gaxios implements FetchCompliance { * This implementation follows the spec: https://www.ietf.org/rfc/rfc2387.txt. However, recursive * multipart/related requests are not currently supported. * - * @param {GaxioMultipartOptions[]} multipartOptions the pieces to turn into a multipart/related body. + * @param {GaxiosMultipartOptions[]} multipartOptions the pieces to turn into a multipart/related body. * @param {string} boundary the boundary string to be placed between each part. */ private async *getMultipartRequest( diff --git a/test/test.getch.ts b/test/test.getch.ts index b05cf101..3db1a3d4 100644 --- a/test/test.getch.ts +++ b/test/test.getch.ts @@ -1214,6 +1214,45 @@ describe('🎏 data handling', () => { scope.done(); } }); + + it('should handle "204 No Content" responses when response type is "json"', async () => { + const scope = nock(url) + .matchHeader('content-type', 'application/json') + .put('/') + .reply(204, '', {'Content-Type': 'application/json'}); + const res = await request({ + url, + method: 'PUT', + data: {}, + headers: new Headers({ + 'content-type': 'application/json', + accept: 'application/json', + }), + responseType: 'json', + }); + scope.done(); + assert.deepStrictEqual(res.data, ''); + }); + + it('should not throw an error in case of invalid json and "json" response type', async () => { + const invalidJsonText = '{foo: 1}', + scope = nock(url) + .matchHeader('content-type', 'application/json') + .put('/') + .reply(200, invalidJsonText, {'Content-Type': 'application/json'}); + const res = await request({ + url, + method: 'PUT', + data: {}, + headers: new Headers({ + 'content-type': 'application/json', + accept: 'application/json', + }), + responseType: 'json', + }); + scope.done(); + assert.deepStrictEqual(res.data, invalidJsonText); + }); }); describe('🍂 defaults & instances', () => { From de51b03e0888b60f73eec3a12e5444c6198ba8dd Mon Sep 17 00:00:00 2001 From: Tomas Rimkus Date: Tue, 7 Oct 2025 10:54:01 +0300 Subject: [PATCH 3/3] chore: fix lint error --- src/gaxios.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gaxios.ts b/src/gaxios.ts index cceeef87..2df23b4c 100644 --- a/src/gaxios.ts +++ b/src/gaxios.ts @@ -276,13 +276,14 @@ export class Gaxios implements FetchCompliance { switch (opts.responseType) { case 'stream': return res.body; - case 'json': + case 'json': { const data = await res.text(); try { return JSON.parse(data); } catch { return data; } + } case 'arraybuffer': return res.arrayBuffer(); case 'blob':