diff --git a/src/__tests__/restLink.ts b/src/__tests__/restLink.ts index 0444f4a..70ba38c 100755 --- a/src/__tests__/restLink.ts +++ b/src/__tests__/restLink.ts @@ -1717,6 +1717,92 @@ describe('Mutation', () => { }); }); + describe('empty response bodies', () => { + afterEach(() => { + fetchMock.restore(); + }); + it('returns an empty object on 204 status', async () => { + // In truth this test is just for show, because the fetch implementation + // used in the tests already returns {} from res.json() for 204 responses + expect.assertions(1); + + const link = new RestLink({ uri: '/api' }); + + const post = { id: '1', title: 'Love apollo' }; + fetchMock.post('/api/posts', { + status: 204, + body: post, + }); + + const createPostMutation = gql` + fragment PublishablePostInput on REST { + title: String + } + + mutation publishPost($input: PublishablePostInput!) { + publishedPost(input: $input) + @rest(type: "Post", path: "/posts", method: "POST") { + id + title + } + } + `; + const response = await makePromise( + execute(link, { + operationName: 'publishPost', + query: createPostMutation, + variables: { input: { title: post.title } }, + }), + ); + + expect(response.data.publishedPost).toEqual({ + __typename: 'Post', + id: null, + title: null, + }); + }); + it('returns an empty object on zero Content-Length', async () => { + // In Node.js parsing an empty body doesn't throw an error, so the best test is + // to provide body data and ensure the zero length still triggers the empty response + expect.assertions(1); + + const link = new RestLink({ uri: '/api' }); + + const post = { id: '1', title: 'Love apollo' }; + fetchMock.post('/api/posts', { + headers: { 'Content-Length': 0 }, + body: post, + }); + + const createPostMutation = gql` + fragment PublishablePostInput on REST { + title: String + } + + mutation publishPost($input: PublishablePostInput!) { + publishedPost(input: $input) + @rest(type: "Post", path: "/posts", method: "POST") { + id + title + } + } + `; + const response = await makePromise( + execute(link, { + operationName: 'publishPost', + query: createPostMutation, + variables: { input: { title: post.title } }, + }), + ); + + expect(response.data.publishedPost).toEqual({ + __typename: 'Post', + id: null, + title: null, + }); + }); + }); + describe('fieldNameDenormalizer', () => { afterEach(() => { fetchMock.restore(); diff --git a/src/restLink.ts b/src/restLink.ts index c86f097..a9e33b1 100755 --- a/src/restLink.ts +++ b/src/restLink.ts @@ -93,7 +93,7 @@ export namespace RestLink { /** * A function that takes the response field name and converts it into a GraphQL compliant name - * + * * @note This is called *before* @see typePatcher so that it happens after * optional-field-null-insertion. */ @@ -113,7 +113,7 @@ export namespace RestLink { * * @note: This is called *after* @see fieldNameNormalizer because that happens * after optional-nulls insertion, and those would clobber normalized names. - * + * * @warning: We're not thrilled with this API, and would love a better alternative before we get to 1.0.0 * Please see proposals considered in https://github.com/apollographql/apollo-link-rest/issues/48 * And consider submitting alternate solutions to the problem! @@ -732,7 +732,9 @@ const resolver: Resolver = async ( }) .then(res => { context.responses.push(res); - return res.json(); + return res.status === 204 || res.headers.get('Content-Length') === '0' + ? {} + : res.json() }) .then( result =>