How to render errors with apollo-link-error - react-hooks

I would like to use apollo client error link to create a MUI snackbar displaying any error that is returned by graphql.
The setup is a Nextjs web app using Material-ui and Apollo Client.
From Apollo documentation on Error links an error link can be created which will handle errors returned by the graphql API. The Apollo error link can not render anything it self as it will return void.
import { onError } from "#apollo/client/link/error";
// Log any GraphQL errors or network error that occurred
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors)
graphQLErrors.forEach(({ message, locations, path }) =>
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
)
);
if (networkError) console.log(`[Network error]: ${networkError}`);
});
My initial thought was to use hooks and context. Creating a hook which the Apollo error link can push notifications on and a context provider in my _app.ts.
However the Apollo client is not a functional component hence this will not work.
I did think about creating a function to handle onError callback in Apollo queries and mutations but it seems like a lot of work to put an onError function on each query/mutation.

This can be handled using context. You have to do some changes to your component hierarchy.
ContextProvider => ApolloProvider
Make sure both are used in different components. Otherwise, you will not able to access hooks.
Ex: You should be able to access the hook inside the Root component, where you can add ApolloProvider.
I've created an example hope that helps (not using apollo but you can add): https://stackblitz.com/edit/react-ts-m7swyo
import React, { createContext, useContext, useReducer } from "react";
import { render } from "react-dom";
import "./style.css";
const Context = createContext({});
const Root = () => {
const { state, dispatch } = useContext(Context);
return (
<ApolloProvider
client={
new ApolloClient({
link: ApolloLink.from([
onError(({ graphQLErrors, networkError }) => {
// Do something and dispatch to context
// dispatch({ type: 'ERROR', error: graphQLErrors || networkError });
}),
]),
})
}
>
<App />
</ApolloProvider>
);
};
const reducer = (state, action) => {
if (action.type === "ERROR") {
return { ...state, ...action.payload };
}
return state;
};
const App = () => {
const [state, dispatch] = useReducer(reducer, {});
return (
<Context.Provider
value={{
state,
dispatch,
}}
>
<Root />
</Context.Provider>
);
};
render(<App />, document.getElementById("root"));

Related

convert data to error in a custom ApolloLink

I'm trying to create a custom ApolloLink that converts specific graphql data to an error which will be passed to the onError link. The omg help! error is successfully passed as a network error to onError, but I also get a run time error in my browser: Uncaught (in promise) Error: omg help! at new ApolloError which should not be happening. How do I make sure the error is entirely handled in onError and not also throwing a runtime error from the TransformErrorLink code?
TransformErrorLink.js:
import { ApolloLink, Observable } from 'apollo-link-core';
class TransformErrorLink extends ApolloLink {
request(operation, forward) {
const observable = forward(operation);
return new Observable((observer) => {
observable.subscribe({
next: (value) => {
// reroute Error types as proper errors
for (const [req, res] of Object.entries(value.data)) {
if (res?.__typename?.includes('Error')) {
observer.error(new Error('omg help!'));
}
}
observer.next(value);
},
error: observer.error.bind(observer),
complete: observer.complete.bind(observer)
});
});
}
}
export default new TransformErrorLink();
the composite set of apollo links:
import { from, HttpLink } from '#apollo/client';
import { onError } from '#apollo/client/link/error';
import transformErrorLink from './transformErrors';
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors)
graphQLErrors.forEach(({ message, locations, path }) =>
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
)
);
if (networkError) console.log(`[Network error]: ${networkError}`);
});
const additiveLink = from([
errorLink,
transformErrorLink,
new HttpLink({ uri: process.env.GRAPH_API_URL })
]);
export default additiveLink;
I tried swapping the order of errorLink and transformErrorLink in the additiveLink, but that resulted in just a runtime error with no console log first.
I have also been reading https://www.apollographql.com/blog/frontend/apollo-link-creating-your-custom-graphql-client-c865be0ce059/ which does the reverse of what I'm doing - convert an error to data.
I haven't used apollo-link-core before, but should you not return observable's projection like in the last line below?
class TransformErrorLink extends ApolloLink {
request(operation, forward) {
const observable = forward(operation);
return new Observable((observer) => {
**return** observable.subscribe({
...
I figured it out - I needed to add an errors property (of type Array) to value and then call observer.next with value instead of calling observer.error with an Error.

apollo-server-lambda: Unable to determine event source based on event

I am using apollo-server-lambda for my app. I have create custom authoization http headers and it is required . if authoization: LETMEIN then it will return true and also return all data, if there is no any authoization or wrong authoization then it wll throw an error. For local development I used serverless-offline.In Local environment, it works as expected and here is the image but when I deploy my code to AWS, the api end does not work. It always throws me the error: here is the link.
I test my function AWS console. I am getting this error:
I did not get what I am doing wrong.
Here is my code
/* eslint-disable #typescript-eslint/no-var-requires */
import { ApolloServerPluginLandingPageGraphQLPlayground } from 'apollo-server-core';
import { ApolloServer, AuthenticationError } from 'apollo-server-lambda';
import schema from '../graphql/schema';
import resolvers from '../resolvers';
import runWarm from '../utils/run-warm';
export const authToken = (token: string) => {
if (token === 'LETMEIN') {
return;
} else {
throw new AuthenticationError('No authorization header supplied');
}
};
const server = new ApolloServer({
typeDefs: schema,
resolvers,
debug: false,
plugins: [ApolloServerPluginLandingPageGraphQLPlayground()],
context: ({ event }) => {
//console.log(context);
if (event.headers) {
authToken(event.headers.authorization);
}
},
});
export default runWarm(
server.createHandler({
expressGetMiddlewareOptions: {
cors: {
origin: '*',
credentials: true,
allowedHeaders: ['Content-Type', 'Origin', 'Accept'],
optionsSuccessStatus: 200,
maxAge: 200,
},
},
})
);
This is my Lambda function
/**
* Running warm functions help prevent cold starts
*/
const runWarm =
(lambdaFunc: AWSLambda.Handler): AWSLambda.Handler =>
(event, context, callback) => {
// Detect the keep-alive ping from CloudWatch and exit early. This keeps our
// lambda function running hot.
if (event.source === 'serverless-plugin-warmup') {
return callback(null, 'pinged');
}
return lambdaFunc(event, context, callback);
};
export default runWarm;
This is not a direct answer, but might help, and could be useful if anyone else (like me) found this thread because of the error "Unable to determine event source based on event" when using apollo-server-lambda.
That error is coming from #vendia/serverless-express which is being used by apollo-server-lambda.
Within serverless-express, in src/event-sources/utils.js, there is a function called getEventSourceNameBasedOnEvent(), which is throwing the error. It needs to find something in the event object, and after a bit of experimentation I found that writing the lambda function like this solved the issue for me:
const getHandler = (event, context) => {
const server = new ApolloServer({
typeDefs,
resolvers,
debug: true,
});
const graphqlHandler = server.createHandler();
if (!event.requestContext) {
event.requestContext = context;
}
return graphqlHandler(event, context);
}
exports.handler = getHandler;
Note that the context object is added to the event object with the key "requestContext"....that's the fix.
(Also note that I have defined typeDefs and resolvers elsewhere in the code)
I can't guarantee this is the ideal thing to do, but it did work for me.

Redux Thunk action creator not dispatching fetching api

I have created a small react-redux application to fetch api data with redux-thunk middleware, for some reason, the action creator function that returns dispatch is not working.
Action Creators:
export const fetchUsers = () => {
console.log('test 1');
return dispatch => {
console.log('test 2');
dispatch(fetchUserRequest);
axios
.get("https://jsonplaceholder.typicode.com/users")
.then(response => {
const users = response.data;
dispatch(fetchUserSuccess(users));
})
.catch(error => {
const errorMessage = error.message;
dispatch(fetchUsersFailure(errorMessage));
});
};
};
console.log('test 1') is working but console.log('test 2') is not working.
Here is codesanbox link
You were missing a a few things:
in userTypes you were missing there is no _ when you create const types for example export const FETCH_USER_REQUEST = "FETCH USER REQUEST"; should be export const FETCH_USER_REQUEST = "FETCH_USER_REQUEST"; also in userActions import it from userTypes not userReducers it should be import {
FETCH_USER_REQUEST,
FETCH_USER_SUCCESS,
FETCH_USER_FAILURE
} from "./userTypes";
I have also fixed your userContainer, codesandbax: codesandbax

How to handle apollo errors in one place with react-hooks?

I'm using React with hooks + GraphQL.
My app.jsx:
import { onError } from 'apollo-link-error';
...
const httpLink = ...
const errorLink = onError(err => console.log(err))
const terminatingLink = split(...httpLink, errorLink)
const client = new ApolloClient({
link: ApolloLink.from([terminatingLink])
...
})
<ApolloProvider client={client}>
<ErrorProvider>
</ErrorProvider>
</ApolloProvider>
Error provider is used as a common state for errors, i.e. if mutation response is bad.
Currently I create onError handler for each(!) mutation and query, i.e. like this:
const [createTeam] = useMutation(createTeamQ, {
onError: (err) => { dispatchError(err) }
})
This looks like overcoding, since I do it too often.
But I can't figure out how to dispatch the error in AppolloClient only once in onError() function imported from 'apollo-link-error'. I can't use hooks there.
Should I use redux for this particular case?
Thanks in advance.

302 Found on Laravel in GET request made by cross-fetch on react

I have a component with a table, when this component mounts I would like to make a request to get data that fills the table.
In my table component:
componentDidMount() {
const { fetchTransactions } = this.props;
fetchTransactions()
}
This came from (I'm using redux):
const mapDispatchToProps = dispatch => ({
fetchTransactions: value => dispatch(fetchTransactions())
})
And the action:
export function fetchTransactions() {
return function (dispatch) {
dispatch(requestTransactions())
return fetch('/getTransactions')
.then(
response => response.json(),
error => console.log('An error occurred.', error)
)
.then(json => dispatch(receiveTransactions(json)))
}
}
When the component mounts, the action is dispatched and the fetch too, but the response is a 302 found as you can see here:
Using the browser it work as I expect:
Any ideas? thank you.

Resources