diff --git a/.changeset/unlucky-geese-give.md b/.changeset/unlucky-geese-give.md new file mode 100644 index 0000000..fa62f6f --- /dev/null +++ b/.changeset/unlucky-geese-give.md @@ -0,0 +1,5 @@ +--- +'@apollo/datasource-rest': patch +--- + +Parse JSON body when Content-Type has charset diff --git a/src/RESTDataSource.ts b/src/RESTDataSource.ts index 9246f54..3babd86 100644 --- a/src/RESTDataSource.ts +++ b/src/RESTDataSource.ts @@ -303,14 +303,14 @@ export abstract class RESTDataSource { protected parseBody(response: FetcherResponse): Promise { const contentType = response.headers.get('Content-Type'); const contentLength = response.headers.get('Content-Length'); + const mediaType = contentType?.split(';')?.[0]; if ( // As one might expect, a "204 No Content" is empty! This means there // isn't enough to `JSON.parse`, and trying will result in an error. response.status !== 204 && contentLength !== '0' && - contentType && - (contentType.startsWith('application/json') || - contentType.endsWith('+json')) + (mediaType?.startsWith('application/json') || + mediaType?.endsWith('+json')) ) { return response.json(); } else { diff --git a/src/__tests__/RESTDataSource.test.ts b/src/__tests__/RESTDataSource.test.ts index f3e7cda..4652c29 100644 --- a/src/__tests__/RESTDataSource.test.ts +++ b/src/__tests__/RESTDataSource.test.ts @@ -784,6 +784,51 @@ describe('RESTDataSource', () => { expect(data).toEqual({ foo: 'bar' }); }); + it('returns data as parsed JSON when Content-Type ends in +json and has charset provided', async () => { + const dataSource = new (class extends RESTDataSource { + override baseURL = 'https://api.example.com'; + + getFoo() { + return this.get('foo'); + } + })(); + + nock(apiUrl) + .get('/foo') + .reply( + 200, + { foo: 'bar' }, + { 'content-type': 'application/vnd.api+json; charset=utf-8' }, + ); + + const data = await dataSource.getFoo(); + + expect(data).toEqual({ foo: 'bar' }); + }); + + it('returns data as parsed JSON when Content-Type ends in +json and has many parameters provided', async () => { + const dataSource = new (class extends RESTDataSource { + override baseURL = 'https://api.example.com'; + + getFoo() { + return this.get('foo'); + } + })(); + + nock(apiUrl).get('/foo').reply( + 200, + { foo: 'bar' }, + { + 'content-type': + 'application/vnd.api+json; charset=utf-8; boundary=ExampleBoundaryString', + }, + ); + + const data = await dataSource.getFoo(); + + expect(data).toEqual({ foo: 'bar' }); + }); + it('returns data as a string when Content-Type is text/plain', async () => { const dataSource = new (class extends RESTDataSource { override baseURL = 'https://api.example.com';