How to Catch Errors from Apollo/GraphQL - error-handling

I use graphCool as a backend, but there is a bug which won't be fixed any time soon.
Making a delete mutation on File schema will always throw this error (not a big deal):
"GraphQL error: Whoops. Looks like an internal server error. Please contact us from the Console (https://console.graph.cool) or via email (support#graph.cool) and include your Request ID:"
The call works, but I need to access the update function after the call and I can't because of the error.
Two solutions:
1. Catch the graphql error so I can reach update: function, how can I do this?
2. Update the store without using the update: function, how can I do this?
Here is my code:
_deleteImageFile = async () => {
// THIS WORKS, BUT GET AN ERROR
const socialPostId = this.props.socialPost.id
// Tried to wrap the following in try catch without success
await this.props.deleteImageFileMutation({
variables: {
id: this.props.socialPost.image.id
}, //Error hits here "internal service error..."
update: (store) => { //This is never run because of error
console.log('this text will never log')
const userId = localStorage.getItem(GC_USER_ID)
const data = store.readQuery({query: ALL_SOCIAL_POSTS_QUERY,
variables: {
id: userId
}})
// Need to change the store here
store.writeQuery({query: ALL_SOCIAL_POSTS_QUERY, data,
variables: {
id: userId
}}).catch(res => { const errors = res.graphQLErrors; console.log(errors)})
//The catch was an attempt to fix it
}
})
}
Reference Bug: https://github.com/graphcool/framework/issues/434

You could try to pass down another dummy mutation and do that after the deleteImageFileMutation throws.
try {
await this.props.deleteImageFileMutation ...
} catch (e) {
// ...
} finally {
await this.props.dummyMutation ...
}

Related

null is not an object (using async storage)

I'm trying to implement secure store (like async storage) into my testproject and it works. The only thing that doesn't work appears to be my load() useEffect that runs every time I start a new session.
So every time I start the session "null is not an object" appears and throws errors at every part where I'm looking for my list on my home-screen (my state is called goals).
but if I temporarily turn it off, add a goal to my list, and then turn it on - it works fine every time I restart the app
I feel like I need to write a condition in my load() statement but I can't figure out what, I think the app runs the load() and gets stuck at the statements where I use goals before I've added any. Can someone help me here? I've tried if (goals !== null) but it doesn't work.
const [goals, setGoals] = useState([])
const load = async() => {
if (goals !== null) {
try {
const goalsValue = await SecureStore.getItemAsync('Goals');
setGoals(JSON.parse(goalsValue))
} catch (err) {
alert(err)
}
}
}
useEffect(()=> {
load()
},[])
So what is happening is that there's nothing inside goalsValue so when you try parsing it you get an error. To avoid that you should add an if statement that checks if it's empty or not
const [goals, setGoals] = useState([])
const load = async() => {
if (goals !== null) {
try {
const goalsValue = await SecureStore.getItemAsync('Goals');
if(goalsValue) setGoals(JSON.parse(goalsValue))
}catch (err) {alert(err)}
}}
useEffect(()=> {
load()
},[])
Try this and let me know if it works ☺️

Express Mongoose throw custom error on callbacks

I'm trying to throw some custom error classes from mongoose callbacks.
Here is a simple code
const Restaurant = require('../models/Restaurant')
const { InternalServerError, UnauthorizedError } = require('../errors/errors')
const checkRestaurantAuthorization = async (token) => {
const restaurant = Restaurant.findOne({ 'token': token }, function (error, result) {
if (error) throw new InternalServerError()
else if (!result) throw new UnauthorizedError()
else return token
})
}
In my code checkRestaurantAuthorization is called by a simple middleware like
const restaurantMidlleware = async (req, res, next) => {
console.log('Request Type:', req.method);
try {
token = await checkRestaurantAuthorization('invalid_token')
next()
} catch (error) {
next(error)
}
}
Now if a restaurant instance with the given token is not found, the app crashes with throw er; // Unhandled 'error' event. From my testing it seems that executions stops when throw new UnauthorizedError() is called and I'm unable to identify the issue.
Here is also an example of a custom defined error if it's useful
class UnauthorizedError extends Error {
constructor(message) {
super(message)
this.name = 'Unauthorized request'
this.code = 403
Error.captureStackTrace(this, UnauthorizedError)
}
}
What am I missing?
have you tried putting your first block in 'try-catch' block?
The throw statement throws a user-defined exception. Execution of the current function will stop (the statements after throw won't be executed), and control will be passed to the first catch block in the call stack. If no catch block exists among caller functions, the program will terminate.
you can change code to promise or async-await
another source of the problem could be the fact that your are using async and callback in one function try to omit async then use it again
And there is no point in writing 'const restaurant =' in
const restaurant = Restaurant.findOne
since every found restaurant will be saved in callback's result variable
try this
function checkRestaurantAuthorization(token){
return new Promise(async(resolve, reject)=>{
try {
const restaurant = await Restaurant.findOne({ 'token': token });
if (!restaurant)
return reject(new UnauthorizedError())
else
return resolve(token)
}catch(error){
return reject(new InternalServerError())
}
})}
Even better approach would be using only async function with try-catch instead of returning a promise or any callback

How to prevent error propagation in Apollo Client useQuery in react?

I'd like to catch the error in component level and prevent propagation while using the useQuery in #apollo/react-hook.
Here is my example code
const invitationDocument = gql`
query DecodeInvitation($token: String!) {
DecodeInvitation(token: $token) {
name
email
}
}
`
const InvitationPage = (props) => {
const { data, error, loading } = useQuery(invitationDocument, {variables: { token: "XXXX" }});
if(error)
{
return <InvitationErrorPage error={error.message}/>
}
return loading? <LoadingPage> : <InvitationAcceptPage />
}
It works fine but at the same time, the error is being propagated to its parents level so I get another error notification message which comes from the error handler at the global level.
At the application level, I use the apollo-link-error to manage the Graphql errors.
import { onError } from 'apollo-link-error';
const errorLink = onError (({ graphqlErrors, networkError }) => {
if(graphqlErrors)
notification.error(graphqlErrors[0].message);
});
const client = ApolloClient({
cache: new InMemoryCache(),
link: ApolloLink.from([
errorLink,
new HttpLink({ uri: `http://localhost:8080/graphql`})
])
})
For now, I am finding a solution to stop propagation to top-level so that I can show only InvitationErrorPage and stop displaying error notification at the global level.
I was also trying to prevent errors from being logged in an Error Link by handling them on a useQuery hook and a further delve into the ApolloLink documentation helped clear up what is happening. The key misunderstanding is that the Error Link is not a parent- or application-level handler, it is request middleware. It's helpful to think about how the data is coming back from the server:
Thus, when you see an error notification from the Error Link it is not something that "propagated up" from the useQuery hook: it occurred in the request path before the useQuery result was available on the client.
Thus, the onError callback for the Error Link will always be called before any error handling code in the useQuery hook.
Probably your best bet is to use a combination of the operation and graphQLErrors[x].extensions to figure out what errors you should pass through the Error Link middleware like so:
const errorLink = onError(({operation, response, graphQLErrors}) => {
if (!graphQLErrors) {
return;
}
if (operation.operationName === "DecodeInvitation") {
for (const err of graphQLErrors) {
if (err.extensions?.code === 'UNAUTHENTICATED') {
// Return without "notifying"
return;
}
}
}
// Notify otherwise
notification.error(graphqlErrors[0].message);
})

Async Await with Mongoose Returns Empty Object

I have my mongoose schema for a user's profile where they can add work experience (currently an array of objects in schema).
I have used the following code to find the user's profile and get the experience object as input then attach it to the array in schema and return the saved profile with experience:
Router.post('/experience',
Passport.authenticate('jwt', {session: false}), async (req, res) => {
try {
const myProfile = await Profile.findOne({user: req.user._id});
if (myProfile) {
const exp = {
title: req.body.title,
company: req.body.company,
location: req.body.location,
from: req.body.from,
to: req.body.to,
isCurrent: req.body.isCurrent,
description: req.body.description
};
// add to Profile experience array
Profile.experience.unshift(exp); // adds to beginning of array
const savedProfile = await Profile.save(); // have also tried myProfile.save() but that doesn't work too
if (savedProfile) {
console.log(savedProfile);
res.json({message: `Profile Updated Successfully`, details: savedProfile})
}
else { throw `Experience Details Not Saved`}
}
} catch (err) { res.json(err); }
});
The problem here is that the response is always an empty object and when I check my database, there is no experience saved. Is this code wrong? Same thing works with Promises but I want to try a new way of doing things.
The async-await pattern is another way to write Promise, the return value of the function is Promise.resolve(result) or Promise.reject(reason) of the whole async.
In the outer function, Router.post in this case, it has to use async-await, or then of Promise pattern, to deal with the returned Promise. Orelse, the async function would not have chance to run, as the returned Promise would be omitted.

Angular GlobalErrorHandler and HttpErrorResponse - Resolver throwing badly formatted HttpErrorResponse

I've created global error handler in my Angular 6 application:
main error handler method:
handleError(error: Error | HttpErrorResponse) {
const router = this.injector.get(Router);
const notificationService = this.injector.get(NotificationsService);
this._logger(error);
if (!navigator.onLine) {
notificationService.displayNotification('error', 'timespan', {heading: 'Internet connection lost!', body: ''});
} else if (error instanceof HttpErrorResponse) {
notificationService.displayNotification('error', 'click', this._httpErrorMessage(error));
} else {
// CLIENT error
router.navigate(['/error-page']);
}
}
Problem:
Many of HTTP service calls are being performed in resolvers:
resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<ClientDetailsModel> {
if (route.params.cif) {
const reqBody = new GetClientDetailsRequestModel({cif: route.params.cif, idWewPrac: this.userContext.getUserSKP()});
return this.clientsService.getClientDetails(reqBody)
.pipe(
map((clientDetails: { customerDetails: ClientDetailsModel }) => {
if (clientDetails.customerDetails) {
return clientDetails.customerDetails;
}
return null;
})
);
}
If Http error occurs in such a call, error received by my global error handler is formed as HttpErrorResponse wrapped inside Error (message of Error is HttpErrorResponse):
Uncaught (in promise): HttpErrorResponse: {"headers":{"normalizedNames":{},"lazyUpdate":null},"status":400,"statusText":"OK","url":"https://...
If Http errors occurs outside of resolvers global error handler works perfectly fine.
To reach my goal (throwing HttpErrorResponse from resolver) I need to specify the way to handle error in error callback inside subscription, but I cannot do it because resolver is the one who manages subscription.
Is there a way to specify how resolver should handle errors?
I would like to avoid manual parsing of these wrapped errors.
I was searching for a solution, but could only create a work-a-round.
This will check for the HttpErrorResponse text and tries to parse the JSON which results into the real error object.
Not great at all, but better then nothing.
handleError(error: any): void {
console.error('Errorhandler catched error: ' + error.message, error);
// We need to have this little hack in oder to access the real error object
// The Angular resolver / promise wraps the error into the message, serialized as json.
// So we extract this error again.
// But first lets check if we actually dealing with an HttpErrorResponse ...
if (error.message.search('HttpErrorResponse: ')) {
// The error includes an HTTPErrorResponse, so we try to parse it's values ...
const regex = new RegExp('^.*HttpErrorResponse:\\s(\\{.*\\})$');
const matches = regex.exec(error.message);
if (matches !== null) {
// matches the regex, convert...
const httpErrorResponse = JSON.parse(matches[1]); // This is now the real error object with all the fields
this.handleHttpErrorResponse(httpErrorResponse);
} else {
// It contains HttpErrorResponse, but no JSON part...
this.toastr.error('There was an unknown communication error',
'Communication error',
{timeOut: 10000});
}
} else {
this.toastr.error('Unknown error occured',
'Well that should not happen. Check the log for more information...',
{timeOut: 10000});
}
}