How to get error body using React Apollo Link Rest - reactjs

I am creating an React app using Apollo Graphql and I am having some trouble with the Apollo Link Rest. The problem is that in the middle of my app I have to make a REST request, however this request can be sucessful and return a status code = 200 or it can fail, returning a status code = 400.
It is important for my app to show the end user an error message that is returned in this request's body in case of a 400 error. However when an error happens the Apollo Link Rest just throws an exception, but it doesn't return response body back to me.
Is there a way for me to get the response body, when there is an error with the Apollo Link Rest? I thought that I could get it from the result variable, but since the Apollo throws an exception, this variable never changes.
Here is my code:
const result = await context.client.query<MyQuery>({
query: MyQuery,
variables: {
input: {
companyId: variables.companyId,
},
},
});
query MyQuery($input: MyQueryInput!) {
myQuery(input: $input) #rest(
type: "QueryResponse",
method: "POST"
path: "v1/my-query"
) {
id
minimumPreparationTime
maximumPreparationTime
extras {
id
optional
key
}
warning
error {
id
type
message
}
}
}

Extending Greg comment and apollo-link-error, you can retrieve the error body from networkError.result.message.

You can use apollo-link-error to catch and handle server errors, network errors, and GraphQL errors. You should be able to conditionally set response.errors if needed.
import { onError } from "apollo-link-error";
const link = onError(({ graphQLErrors, networkError, response, operation }) =>
{
if (graphQLErrors)
graphQLErrors.map(({ message, locations, path }) =>
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path:
${path}`,
),
);
if (networkError) console.log(`[Network error]: ${networkError}`);
response.error = // set error property here
});
It may also be worth noting Apollo exposes an errorPolicy option, for which the default is none. The policy can be modified to all which, according to the docs,
is the best way to notify your users of potential issues while still
showing as much data as possible from your server. It saves both data
and errors into the Apollo Cache so your UI can use them.

Related

How to get request headers for the Apollo GraphQL error?

I need to get the request header on the client when an error occurs
const { data, error } = useQuery(gql`Query ...`);
// ..
if (error) {
// get the request headers here
// e.g. error.graphQLErrors[0].headers.requestId
}
I was trying to modify the error using onError link but this object is Read Only
import { onError } from '#apollo/client/link/error';
const errorLink = onError((errorHandler) => {
errorHandler.graphQLErrors[0].extensions = { requestId: ... }
})
I know I can extend extensions on the backend side but I need to generate the request ID on the client side.
There's an example similar to what you're describing on the Apollo docs. It doesn't use extensions specifically, but would enable you to generate the request ID mentioned.

ReactJS testing causing a typeError: network request failed

I have been trying to simulate file-upload as a test for my react-app but that was generating the following error :
TypeError: Network request failed
at node_modules/whatwg-fetch/dist/fetch.umd.js:535:18
at Timeout.task [as _onTimeout] (node_modules/jsdom/lib/jsdom/browser/Window.js:516:19)
This is my test: trying to upload a file and check if an alert is raised.
test("triggers alert when receiving a file with inadequate content", async () => {
renderComponent();
global.alert = jest.fn();
const fileContent = raw("./file.kml");
const fakeFile = new File(
[fileContent],
"file.kml",
{ type: "text/xml" }
);
const selectType = screen.getByTestId("select-type");
await fireEvent.change(selectType, { target: { value: "type" } });
const fileUploader = screen.getByTestId("file-uploader");
await fireEvent.change(fileUploader, {
target: { files: [fakeFile] },
});
await waitFor(() => {
expect(global.alert).toHaveBeenCalledWith(
"alert"
);
});
});
});
I am kind of confused, because file is received and parsed by the component and it raise the alert I need to check but still fails because of the network error.
PS: I tried to mock a fetch but still have the same problem.
Any help would be appreciated.
I have been getting the same
Network request failed: Connection refused
error while testing.
I have explored many threads but no luck.
Out of the blue starting the back end server worked for me. (Even though I used mock service worker that intercepts network calls, starting the back end server worked.)
I don't know why.
Also, I have used import fetch from 'isomorphic-fetch'; in setup-test.js instead of whatwg-fetch.
In the end try adding a timeout:3000 option to your waitFor.

Why I got error: Cannot query field xx on type "Query"?

Although I copied and pasted the graphQL query from the GraphiQL tool after I tested it at GraphiQL successfully , the query returned with an error when I tried it in Apollo client within a reactJS app:
[GraphQL error]: Message: Cannot query field "allStudents" on type "Query"., Location: [object Object], Path: undefined
Here is my implementation:
const link = createHttpLink({
uri: 'http://localhost:8000/graphql',
fetchOptions: { method: "POST" }
});
const client = new ApolloClient({
link: link
});
const GET_STUDENTS = gql`
query getStudents($schoolID: Int!){
allStudents(schoolId: $schoolID){
pickUpLat
pickUpLng
}
}
`;
client
.query({
query: GET_STUDENTS,
variables: { schoolID: 1 }
})
.then(result => console.log(result));
What could be wrong? here is the correct response that I expected:
{
"data": {
"allStudents": [
{
"pickUpLat": 31.9752942479727,
"pickUpLng": 35.8438429235775
},
{
"pickUpLat": 31.9754545979993,
"pickUpLng": 35.8437478537235
}
]
}
}
EDIT
I get expected results using GraphiQL:
EDIT2
I tried to compare the payload between my request and GraphiQL request:
My request's payload: ( it has __typename which I don't know why )
{"operationName":"getStudents","variables":{"schoolID":1},"query":"query getStudents($schoolID: Int) {\n allStudents(schoolId: $schoolID) {\n pickUpLat\n pickUpLng\n __typename\n }\n}\n"}
GraphiQL request's payload:
{"query":"query getStudents($schoolID: Int!){\n allStudents(schoolId: $schoolID){\n pickUpLat\n pickUpLng\n }\n}","variables":{"schoolID":1},"operationName":"getStudents"}
So, they are almost identical, Any idea?
The fault with my query was that I didn't download the new schema.
You can download the schema by using: apollo schema:download --endpoint=http://localhost:8080/graphql schema.json
replace http://localhost:8080/graphql with your server endpoint
You can see more at https://www.apollographql.com/docs/ios/downloading-schema/
In my case, I had defined a query which didn't require any parameters and it would return an array of objects:
myBasicQuery: [BasicAnswer]
type BasicAnswer {
name String
phone String
}
I was getting the error: Cannot query field \"BasicAnswer\" on type \"BasicAnswer\" when I declared it like this:
query myBasicQuery {
BasicAnswer {
name
phone
}
}
Leaving only the fields of BasicAnswer fixed the issue:
query myBasicQuery {
name
phone
}
For anyone else who might be searching for this problem, make sure you're importing ApolloClient from apollo-client package and not other packages.
For anyone that follows apollo-client has now been sunset, in favour of #apollo/client
Here is how you should be constructing a query
Updated #graphql-codegen/cli package to latest version(2.6.2) and it's working fine.
For anybody who use hasura also.
Problem was
query: query_root
# query: Query # wrong value before upgrade
mutation: mutation_root
subscription: subscription_root
}```
Take a look at your entity, it may be missing the #Field() annotation.
Example:
#PrimaryGeneratedColumn()
id: string;//Will not be mapped by GraphQL and you will get OP's error
#Column()
#Field()
name: string;//Will be mapped by GraphQL and you will not get OP's error
This also happens, if you have fragmented your graphqls files and you forgot to define it in codegen.ts.
(java-project)/src/main/resources:
schema.graphqls
search.graphqls
(angular-project)/codegen.ts
const codegen: CodegenConfig = {
overwrite: true,
// add wildcards here, to read all files:
schema: "../projectname/src/main/resources/graphql/*.graphqls",
documents: './src/app/core/graphql/*.ts',
generates: {
....
}
}
If you see this issue in the GraphiQL interface, you may need to merely refresh the page, which downloads the new schema and the error goes away.

How to handle api errors using aws-amplify?

I'm currently trying to POST data to my aws lambda functions triggered by aws api-gateway using the aws-amplify react lib.
Here is the code :
API.post("snippets","snippets/", {
body: data,
}).then(response => response).catch(console.log(err))
In the main case, everything is OK.
But my lambda function is design to validate the input data and return a status code 400 with a returned payload looking like that :
{
"errors": [
{
"field": "title",
"message": "This field is required"
}
]
}
I would like to catch those errors in order to display them in the frontend but aws-amplify seems to have an undocumented behavior.
By default, status code 400 returned are throw with a default error message :
Error: Request failed with status code 400
at createError (createError.js:16)
at settle (settle.js:18)
at XMLHttpRequest.handleLoad (xhr.js:77)
Is there a way to get the returned payload instead of this magical error?
It turns out that under the hood, aws-amplifyuse Axios to make http calls.
When using Axios, you have to console.log(error.response): https://github.com/axios/axios/issues/960
Here is the fix I've made :
API.post("snippets","snippets/", {
body: data,
}).then(response => response).catch(error => console.log(error.response.data))
A Pull Request on the aws-amplify documentation is open : https://github.com/aws/aws-amplify/pull/633
I also faced the similar issues, It showed the default error message "Request failed with status code 400", instead of the message that is returned from API.
I logged the Error object and it did not show the response attribute in it. But we do have response attribute. I tried logging the Error.response and it did contain the response sent from the API.
Just figured out this by going through the 'Cancel API requests' Amplify docs.
From what I can see this is the contents of the error object returned by the API call:
Heres what I am doing to just print out the error, obviously you would do a lot more here but its a good start.
async uploadUser(state, payload) {
const promise = API.graphql({
query: createUser,
variables: { input: payload },
});
try {
await promise;
} catch (error) {
// Print out the actual error given back to us.
console.log(error.errors[0].message);
// If the error is because the request was cancelled we can confirm here.
if (API.isCancel(error)) {
// handle user cancellation logic.
console.log(error.message);
}
}
Hope that helps 😃

How should we handle an error while getting initial state from store in getDataFromTree

What I am trying to do
Render a page server side with data from our graphql server with errors.
Context
Our GraphQL server speaks to multiple APIs and returns a graphQL response which is handled by Apollo UI.
Certain GraphQL errors are not considered critical, and we want to still render a page with it. E.g.
data: {
thing: {
id: "1234"
name: "Thing"
nonCriticalData: null
},
error: {
graphQLErrors: [
path: ['thing', 'nonCriticalData']
]
}
}
With the above response we get from Apollo, we want to still render a page with thing. Looking at the implementation of getDataFromTree here, any error will get thrown, without the rest of the data.
How we plan to handle our errors
Here is a snippet of how we plan to handle our code:
getDataFromTree(app)
.then(() => {})
.catch(error => {
const content = ReactDOMServer.renderToString(app);
const { data } = client.getInitialState();
console.log("data", data); <-------------- We need data here
console.log("error", error); <———————— errors are here
// render page with data
});
Please let me know if there are ways around this, or whether I have missed
something.
Extra questions:
Should getDataFromTree even throw errors? It feels like the responsibility should lie with the consumer.

Resources