Repository: FormidableLabs/urql
Pull request #957 (2020-09-04 13:21:49)

(core) - Omit Content-Type header when using GET
<!--
  Thanks for opening a pull request! We appreciate your dedication and help!
  Before submitting your pull request, please make sure to read our CONTRIBUTING guide.

  The best contribution is always a PR! However, if you're starting to work on a large
  change, it's best to make sure to open an issue first.

  If this PR is already related to an issue, please reference it like so:
  Resolves #123
-->

Fixes: https://github.com/FormidableLabs/urql/issues/956

## Summary

Remove the `content-type` from the headers when using GET.

## Set of changes

- Remove the `content-type` from the headers when using GET.


changeset-bot[bot]: ###  🦋  Changeset is good to go

Latest commit: e5ad1448c14d7deb533d6854e5179f1b3a5e7cd9

**We got this.**

<details><summary>This PR includes changesets to release 3 packages</summary>

  | Name                           | Type  |
| ------------------------------ | ----- |
| @urql/core                     | Patch |
| @urql/exchange-multipart-fetch | Patch |
| @urql/exchange-persisted-fetch | Patch |

</details>

Not sure what this means? [Click here  to learn what changesets are](https://github.com/changesets/changesets/blob/master/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add another changeset to this PR](https://github.com/FormidableLabs/urql/new/dont-set-content-type-for-get?filename=.changeset/clean-wombats-care.md&value=---%0A%22%40urql%2Fcore%22%3A%20patch%0A---%0A%0Aremove%20content-type%20when%20using%20get%0A)



Patch:
diff --git a/.changeset/pink-crabs-clean.md b/.changeset/pink-crabs-clean.md
new file mode 100644
index 00000000..7879ed64
--- /dev/null
+++ b/.changeset/pink-crabs-clean.md
@@ -0,0 +1,5 @@
+---
+'@urql/core': patch
+---
+
+Fix condition where mutated result data would be picked up by the `ssrExchange`, for instance as a result of mutations by Graphcache. Instead the `ssrExchange` now serializes data early.
diff --git a/.changeset/pretty-pillows-serve.md b/.changeset/pretty-pillows-serve.md
new file mode 100644
index 00000000..a5728acb
--- /dev/null
+++ b/.changeset/pretty-pillows-serve.md
@@ -0,0 +1,7 @@
+---
+'@urql/core': patch
+'@urql/exchange-multipart-fetch': patch
+'@urql/exchange-persisted-fetch': patch
+---
+
+Omit the `Content-Type: application/json` HTTP header when using GET in the `fetchExchange`, `persistedFetchExchange`, or `multipartFetchExchange`.
diff --git a/docs/README.md b/docs/README.md
index 70230ff8..59795ef3 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -6,7 +6,7 @@ order: 0
 # Quick start
 
 `urql` is a blazingly fast, lightweight, and customisable GraphQL client. We believe in usability and adaptability, `urql` is built with extensibility at its
-core, allowing you to to include just the key logic for a basic app, in addition to being able to grow to support dynamic single-app applications and highly
+core, allowing you to include just the key logic for a basic app, in addition to being able to grow to support dynamic single-app applications and highly
 customised infrastructure.
 
 In this page we'll take a look at the quickest route to get up and running with `urql`.
diff --git a/packages/core/src/exchanges/ssr.ts b/packages/core/src/exchanges/ssr.ts
index 53a56025..edb21646 100644
--- a/packages/core/src/exchanges/ssr.ts
+++ b/packages/core/src/exchanges/ssr.ts
@@ -4,7 +4,7 @@ import { Exchange, OperationResult, Operation } from '../types';
 import { CombinedError } from '../utils';
 
 export interface SerializedResult {
-  data?: any;
+  data?: string | undefined; // JSON string of data
   error?: {
     graphQLErrors: Array<Partial<GraphQLError> | string>;
     networkError?: string;
@@ -35,7 +35,11 @@ const serializeResult = ({
   data,
   error,
 }: OperationResult): SerializedResult => {
-  const result: SerializedResult = { data, error: undefined };
+  const result: SerializedResult = {
+    data: JSON.stringify(data),
+    error: undefined,
+  };
+
   if (error) {
     result.error = {
       graphQLErrors: error.graphQLErrors.map(error => {
@@ -59,10 +63,11 @@ const deserializeResult = (
   operation: Operation,
   result: SerializedResult
 ): OperationResult => {
-  const { error, data } = result;
+  const { error, data: dataJson } = result;
+
   const deserialized: OperationResult = {
     operation,
-    data,
+    data: dataJson ? JSON.parse(dataJson) : undefined,
     extensions: undefined,
     error: error
       ? new CombinedError({
diff --git a/packages/core/src/internal/fetchOptions.ts b/packages/core/src/internal/fetchOptions.ts
index b14743e7..244f3592 100644
--- a/packages/core/src/internal/fetchOptions.ts
+++ b/packages/core/src/internal/fetchOptions.ts
@@ -87,9 +87,8 @@ export const makeFetchOptions = (
     ...extraOptions,
     body: !useGETMethod && body ? JSON.stringify(body) : undefined,
     method: useGETMethod ? 'GET' : 'POST',
-    headers: {
-      'content-type': 'application/json',
-      ...extraOptions.headers,
-    },
+    headers: useGETMethod
+      ? extraOptions.headers
+      : { 'content-type': 'application/json', ...extraOptions.headers },
   };
 };


Test patch:
diff --git a/packages/core/src/exchanges/ssr.test.ts b/packages/core/src/exchanges/ssr.test.ts
index f8e14abb..d2b3e161 100644
--- a/packages/core/src/exchanges/ssr.test.ts
+++ b/packages/core/src/exchanges/ssr.test.ts
@@ -2,7 +2,7 @@ import { makeSubject, pipe, map, publish, forEach, Subject } from 'wonka';
 
 import { Client } from '../client';
 import { queryOperation, queryResponse } from '../test-utils';
-import { ExchangeIO, Operation } from '../types';
+import { ExchangeIO, Operation, OperationResult } from '../types';
 import { CombinedError } from '../utils';
 import { ssrExchange } from './ssr';
 
@@ -12,6 +12,11 @@ let client: Client;
 let input: Subject<Operation>;
 let output;
 
+const serializedQueryResponse = {
+  ...queryResponse,
+  data: JSON.stringify(queryResponse.data),
+};
+
 beforeEach(() => {
   input = makeSubject<Operation>();
   output = jest.fn();
@@ -35,7 +40,43 @@ it('caches query results correctly', () => {
 
   expect(data).toEqual({
     [queryOperation.key]: {
-      data: queryResponse.data,
+      data: serializedQueryResponse.data,
+      error: undefined,
+    },
+  });
+});
+
+it('serializes query results quickly', () => {
+  const queryResponse: OperationResult = {
+    operation: queryOperation,
+    data: {
+      user: {
+        name: 'Clive',
+      },
+    },
+  };
+
+  const serializedQueryResponse = {
+    ...queryResponse,
+    data: JSON.stringify(queryResponse.data),
+  };
+
+  output.mockReturnValueOnce(queryResponse);
+
+  const ssr = ssrExchange();
+  const { source: ops$, next } = input;
+  const exchange = ssr(exchangeInput)(ops$);
+
+  publish(exchange);
+  next(queryOperation);
+  queryResponse.data.user.name = 'Not Clive';
+
+  const data = ssr.extractData();
+  expect(Object.keys(data)).toEqual(['' + queryOperation.key]);
+
+  expect(data).toEqual({
+    [queryOperation.key]: {
+      data: serializedQueryResponse.data,
       error: undefined,
     },
   });
@@ -62,7 +103,7 @@ it('caches errored query results correctly', () => {
 
   expect(data).toEqual({
     [queryOperation.key]: {
-      data: null,
+      data: 'null',
       error: {
         graphQLErrors: ['Oh no!'],
         networkError: undefined,
@@ -104,7 +145,7 @@ it('resolves cached query results correctly', () => {
   const onPush = jest.fn();
 
   const ssr = ssrExchange({
-    initialState: { [queryOperation.key]: queryResponse as any },
+    initialState: { [queryOperation.key]: serializedQueryResponse as any },
   });
 
   const { source: ops$, next } = input;
@@ -124,7 +165,7 @@ it('deletes cached results in non-suspense environments', async () => {
   const onPush = jest.fn();
   const ssr = ssrExchange();
 
-  ssr.restoreData({ [queryOperation.key]: queryResponse as any });
+  ssr.restoreData({ [queryOperation.key]: serializedQueryResponse as any });
   expect(Object.keys(ssr.extractData()).length).toBe(1);
 
   const { source: ops$, next } = input;
diff --git a/packages/react-urql/src/test-utils/ssr.test.tsx b/packages/react-urql/src/test-utils/ssr.test.tsx
index 4083cead..3342429d 100644
--- a/packages/react-urql/src/test-utils/ssr.test.tsx
+++ b/packages/react-urql/src/test-utils/ssr.test.tsx
@@ -149,7 +149,12 @@ describe('client-side rehydration', () => {
   });
 
   it('can rehydrate results on the client', async () => {
-    ssr.restoreData({ [queryOperation.key]: queryResponse });
+    ssr.restoreData({
+      [queryOperation.key]: {
+        ...queryResponse,
+        data: JSON.stringify(queryResponse.data),
+      },
+    });
 
     expect(() => {
       pipe(client.executeRequestOperation(queryOperation), publish);