Hello,
I have been trying to mutate the response data using ApolloLink, I have tried several workarounds using the following sandbox: https://codesandbox.io/s/practical-fire-vp1li?file=/src/MyApolloClient.js
Sample link to modify response data:
const mutatorLink = new ApolloLink((operation, forward) => {
return forward(operation).map(async (response) => {
await someAsyncLogic(response);
return response;
});
});
Intended outcome:
The application should not crash and the response data should include "status" as one of the keys.
Actual outcome:
The application crashed but the response data included the "status" key.
How to reproduce the issue:
Use the attached sandbox to reproduce this issue. There exists several links in the 'MyApolloClient.js' file, simply disable/enable any of the 3 links.
Enabling link 1 or 3 will end up in throwing an error that's related to storing the item in the cache:
Error writing result to store for query: {"kind":"Document","definitions":
[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetMedia"},"variableDefinitions":
[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":
{"kind":"Name","value":"Page"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"page"},"value":
{"kind":"IntValue","value":"1"}},{"kind":"Argument","name":{"kind":"Name","value":"perPage"},"value":
{"kind":"IntValue","value":"3"}}],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":
{"kind":"Name","value":"pageInfo"},"arguments":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":
[{"kind":"Field","name":{"kind":"Name","value":"total"},"arguments":[],"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"currentPage"},"arguments":[],"directives":[]},{"kind":"Field","name":
{"kind":"Name","value":"lastPage"},"arguments":[],"directives":[]},{"kind":"Field","name":
{"kind":"Name","value":"hasNextPage"},"arguments":[],"directives":[]},{"kind":"Field","name":
{"kind":"Name","value":"perPage"},"arguments":[],"directives":[]}]}},{"kind":"Field","name":
{"kind":"Name","value":"media"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"search"},"value":
{"kind":"StringValue","value":"Fate/Zero","block":false}}],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":
[{"kind":"Field","name":{"kind":"Name","value":"id"},"arguments":[],"directives":[]},{"kind":"Field","name":
{"kind":"Name","value":"title"},"arguments":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":
[{"kind":"Field","name":{"kind":"Name","value":"romaji"},"arguments":[],"directives":[]}]}}]}}]}}]}}],"loc":{"start":0,"end":271}}
undefined is not an object (evaluating 'result[resultFieldKey]')
^ undefined is not an object (evaluating 'result[resultFieldKey]'
Enabling only link 2 will modify the response successfully without a crash, however, it is stuck in the loading state.
Versions
apollo-client: 2.6.10
Apollo-boost: 0.4.9
apollo-link: 1.2.14
apollo-link-http: 1.5.17
--
I have tried find some answers on how to properly manipulate the response using async/await or promises, with no result.. Any help on this would be grateful.
@alichry Unfortunately Observable.prototype.map does not handle async functions automatically, so the Promise object is getting passed back to the client, instead of the result. I agree it would be convenient if you could use an async function here, but the Observable API would need to change, and I don't have control over that.
Although you're still using Apollo Client 2.x, you might be interested in the asyncMap helper function that we use internally, which you could use as follows:
// NOTE: This requires installing @apollo/client (AC3):
import { asyncMap } from "@apollo/client/utilities";
const mutatorLink = new ApolloLink((operation, forward) => {
return asyncMap(forward(operation), async (response) => {
await someAsyncLogic(response);
return response;
});
});
If that works, and you don't want to include @apollo/client/utilities in your bundle, you can copy/paste the asyncMap code into your project—just make sure you have Observable in scope where asyncMap is defined.
@benjamn Thank you for your significant input. Yes, it did work and this solution is convenient for me. I am hoping to contribute to the docs to include this workaround, to help future readers.
Hello again @benjamn đź‘‹
The upcoming editorial pass specifies that it is possible to add new fields to the response object, however, I was not able to do so. Would you please confirm? So we can have an accurate documentation.
Please see this sandbox: https://codesandbox.io/s/nice-buck-rgl4r?file=/src/MyApolloClient.js
Most helpful comment
@benjamn Thank you for your significant input. Yes, it did work and this solution is convenient for me. I am hoping to contribute to the docs to include this workaround, to help future readers.