GraphQL Error Handling with ApolloClient and Apollo-Server-Express - node.js

The onError function from apollo-link-error has a populated graphQLErrors Object, but when ApolloClient throws an error object, the graphQLErrors property contains an empty array. Further inspection reveals the graphql error message in error.networkError.result.errors.
How can one properly configure Apollo Client to return a populated graphQLErrors object?
Apollo Client Setup:
const {ApolloClient} = require('apollo-client')
const { ApolloLink } = require('apollo-link')
const {HttpLink} = require('apollo-link-http')
const {onError} = require('apollo-link-error')
const {InMemoryCache} = require('apollo-cache-inmemory')
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
graphQLErrors.map(({ message, locations, path }) =>
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
),
)
}
if (networkError) console.log(`[Network error]: ${networkError}`)
})
const middleware = (req, ignored_res, next) => {
const client = new ApolloClient({
link: ApolloLink.from([errorLink, new HttpLink({ uri:'http://somegraphqlserver.com', fetch: require('node-fetch') })]),
cache: new InMemoryCache(),
})
req.GQLClient = client
return next()
}
module.exports = middleware
Calling apollo-server-express:
req.GQLClient
.query({
query: SOME_MALFORMED_QUERY,
})
.then((data) => {...})
.catch((error) => {
console.log('rawError', error)
console.log('error.networkError.result.errors', error.networkError.result.errors)
return next(error)
})
Console Results:
[GraphQL error]: Message: Cannot query field "blah" on type "CustomerList"., Location: [object Object], Path: undefined
[Network error]: Error: Response not successful: Received status code 400
rawError { Error: Network error: Response not successful: Received status code 400
at new ApolloError (/.../node_modules/apollo-client/bundle.umd.js:121:28)
at /.../node_modules/apollo-client/bundle.umd.js:1187:41
at /.../node_modules/apollo-client/bundle.umd.js:1620:17
at Array.forEach (<anonymous>)
at /.../node_modules/apollo-client/bundle.umd.js:1619:18
at Map.forEach (<anonymous>)
at QueryManager.broadcastQueries (/.../node_modules/apollo-client/bundle.umd.js:1614:22)
at /.../node_modules/apollo-client/bundle.umd.js:1114:31
at process._tickCallback (internal/process/next_tick.js:178:7)
graphQLErrors: [],
networkError:
{ Error: Response not successful: Received status code 400
at throwServerError (/.../node_modules/apollo-link-http-common/lib/bundle.umd.js:33:21)
at /.../node_modules/apollo-link-http-common/lib/bundle.umd.js:58:17
at process._tickCallback (internal/process/next_tick.js:178:7)
response:
Response {
size: 0,
timeout: 0,
[Symbol(Body internals)]: [Object],
[Symbol(Response internals)]: [Object] },
statusCode: 400,
result: { errors: [Array] } },
message: 'Network error: Response not successful: Received status code 400',
extraInfo: undefined }
error.networkError.result.errors [ { message: 'Cannot query field "blah" on type "CustomerList".',
locations: [ [Object] ] } ]
library versions:
Server:
"apollo-server-express": "^1.3.2"
Client:
"apollo-cache-inmemory": "^1.1.12"
"apollo-client": "^2.2.8"
"apollo-link-error": "^1.0.9"
"apollo-link-http": "^1.5.4"

This is by design. From the docs:
graphQLErrors: An array of errors from the GraphQL endpoint
networkError: Any error during the link execution or server response,
that wasn't delivered as part of the errors field in the GraphQL result
In other words, if your query is malformed, you request a field that isn't valid (like in your example), or hit any other issue that results in status other than 200, the error will appear as part of the networkError property. On the other hand, if the request returns a 200, but the errors array inside the response is populated, those same errors will be returned as part of graphQLErrors.
If you want to see an example of graphQLErrors being populated, format your query correctly but have one of your resolvers throw an error as soon as it's called. As long as the query doesn't hit any other issues, you should see the same error pop up inside graphQLErrors.

Related

Internal Error to send FCM notification to web application

I'm developing an internal client for triggering firebase cloud messaging push notifications.
With android devices it works without problems. however, when I try to send a notification to a pwa I get the message: FirebaseMessagingError: An internal error has occurred. Please retry the request
This is the code of the function I'm developing:
import admin from '../classes/FirebaseNotification.js';
const priority = 'high';
class FormatNotificationAndroid {
static sendNotification = (data) => {
let registrationTokens = data.devices;
let message = {
notification: {
title: data.notification.title,
body: data.notification.message,
}
};
let options = {
priority: priority,
timeToLive: data.notification.time_to_live
};
console.log(registrationTokens);
console.log(message);
console.log(options);
admin.messaging().sendToDevice(registrationTokens, message, options)
.then(response => {
console.log(response.results);
})
.catch(error => {
console.log(error);
});
}
}
export default FormatNotificationAndroid;
The error trace:
error: FirebaseMessagingError: An internal error has occurred. Please retry the request.
at FirebaseMessagingError.fromServerError (C:\projetos\firebase-send-message\node_modules\firebase-admin\lib\utils\error.js:254:16)
at C:\projetos\firebase-send-message\node_modules\firebase-admin\lib\messaging\messaging.js:93:65
at Array.forEach (<anonymous>)
at mapRawResponseToDevicesResponse (C:\projetos\firebase-send-message\node_modules\firebase-admin\lib\messaging\messaging.js:89:26)
at C:\projetos\firebase-send-message\node_modules\firebase-admin\lib\messaging\messaging.js:344:24
at process.processTicksAndRejections (node:internal/process/task_queues:95:5) {
errorInfo: [Object],
codePrefix: 'messaging'

Axios request working fine by itself but when I try to integrate it with my discord bot it always returns an error?

const { SlashCommandBuilder } = require("#discordjs/builders");
const axios = require("axios")
const { XRapidAPIKey, XRapidAPIHost} = require("../config.json");
module.exports = {
data: new SlashCommandBuilder()
.setName('stock')
.setDescription('Replies with the name of the stock and its current price.')
.addStringOption(option =>
option.setName('ticker')
.setDescription('The stock you want the price of.')
.setRequired(true)),
async execute(interaction) {
const ticker = interaction.options.getString('ticker');
const options = {
method: 'GET',
url: `https://realstonks.p.rapidapi.com/${ticker.toUpperCase()}`,
headers: {
"X-RapidAPI-Key": XRapidAPIKey,
"X-RapidAPI-Host": XRapidAPIHost
}
};
axios.request(options).then(function(response) {
interaction.reply(response.data);
}).catch(function(error) {
console.log(error);
});
}
this is the code for the stock price command but it does not work due to an error being thrown. It should send a message in my discord server with the json data from the axios request. It at first didn't work because I tried to put it in a function and return it but I realized that it needs to be in the command function but I cannot figure out why the request is returning a http 400 code even though it works fine by itself.
DiscordAPIError: Cannot send an empty message
at RequestHandler.execute (/Users/lukepitstick/personal/js/stocktrader/node_modules/discord.js/src/rest/RequestHandler.js:350:13)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async RequestHandler.push (/Users/lukepitstick/personal/js/stocktrader/node_modules/discord.js/src/rest/RequestHandler.js:51:14)
at async CommandInteraction.reply (/Users/lukepitstick/personal/js/stocktrader/node_modules/discord.js/src/structures/interfaces/InteractionResponses.js:103:5) {
method: 'post',
path: '/interactions/986055380461301820/aW50ZXJhY3Rpb246OTg2MDU1MzgwNDYxMzAxODIwOnY5c1QzbDdIWVN0NW4zY2d4ODd6M2RRSHA1OVpORHZ5SmFXUkNyY0pKZDNpNGNyVjFtQzZ5UjFYck96ZHhJUVlVNjJ6YWNHRTBFbFBSczlWWXBTdHk2YzFuNFJrRFU5R3R3dDAyQ3ByRmtiaTBpRm1NYUVyRXVHYjhsUk84T25t/callback',
code: 50006,
httpStatus: 400,
requestData: {
json: {
type: 4,
data: {
content: undefined,
tts: false,
nonce: undefined,
embeds: undefined,
components: undefined,
username: undefined,
avatar_url: undefined,
allowed_mentions: undefined,
flags: undefined,
message_reference: undefined,
attachments: undefined,
sticker_ids: undefined
}
},
files: []
}
}
But when I run the code in its own file without the discord related stuff and console.log the data it outputs the json data as you would expect.

How to get/log/capture the errors from a graphql apollo client query

I am interfacing with a graphql backend using the #apollo/client.
The request i am making returns a 400 bad request and in the network tab i can see the json of the errors.
This is what i would like to log in my code but i am uanble to.
try {
const response = await GraphQLClient.query({
query: GET_PERSON,
variables: {
personId: id,
},
errorPolicy: "all",
});
console.log("response", response);
} catch (err) {
console.log("err", err);
}
When i execute the above it goees into the catch block and i do not have access to the errors object.
err Error: Response not successful: Received status code 400
at new ApolloError (index.ts:54)
at QueryManager.ts:1073
at both (asyncMap.ts:30)
at asyncMap.ts:19
at new Promise ()
at Object.then (asyncMap.ts:19)
at Object.error (asyncMap.ts:31)
at notifySubscription (module.js:137)
at onNotify (module.js:176)
at SubscriptionObserver.error (module.js:229)
at iteration.ts:13
at Array.forEach ()
at iterateObserversSafely (iteration.ts:13)
at Object.error (Concast.ts:185)
at notifySubscription (module.js:137)
at onNotify (module.js:176)
at SubscriptionObserver.error (module.js:229)
at createHttpLink.ts:203
graphqlservice
import { ApolloClient, InMemoryCache } from "#apollo/client";
import { Config } from "./../config";
const FRONTEND_API = `${Config.frontend_api}/graphql` || "";
export const GraphQLClient = new ApolloClient({
uri: FRONTEND_API,
cache: new InMemoryCache(),
}
To get the errors as a json response in the catch method.
console.log(err.networkError.result.errors);
Still very unsure why the response object has an error and errors property and i don't know when these are accessible, maybe someone else could shed some light on that.
export declare type ApolloQueryResult<T> = {
data: T;
errors?: ReadonlyArray<GraphQLError>;
error?: ApolloError;
loading: boolean;
networkStatus: NetworkStatus;
partial?: boolean;
};

mailgun not sending email returning undefined data

I am trying to implement a simple email confirmation on signup flow using Angular 4, nodejs, mailgun-js and mailgun. The problem is the mailgun send:
mailgun.messages().send(data, function(error, body) {
console.log(body);
});
is timing out with the following error on the front-end:
POST http://localhost:3000/users/sendMail net::ERR_EMPTY_RESPONSE
core.js:1448 ERROR
HttpErrorResponse {headers: HttpHeaders, status: 0, statusText: "Unknown Error", url: null, ok: false, …}
error
:
ProgressEvent {isTrusted: true, lengthComputable: false, loaded: 0, total: 0, type: "error", …}
headers
:
HttpHeaders {normalizedNames: Map(0), lazyUpdate: null, headers: Map(0)}
message
:
"Http failure response for (unknown url): 0 Unknown Error"
name
:
"HttpErrorResponse"
ok
:
false
status
:
0
statusText
:
"Unknown Error"
url
:
null
__proto__
:
HttpResponseBase
The back-end show now errors but the return value (body) is undefined and the email is not sent. This is my complete back-end sendMail module:
const api_key = '<key-f40360259dea7c667ca06d4aee956421>';
const DOMAIN = '<sandbox836cdf3cfe9c467b916fe5bb0d73e7e7.mailgun.org>';
const mailgun = require('mailgun-js')({ apiKey: api_key, domain: DOMAIN });
// ---------------- sendMail variables -----
router.post('/sendMail', (req, res, next) => {
console.log("Email message data: " + JSON.stringify(req.body));
const data = {
from: 'xxxxdev#gmail.com',
to: [req.body.email, 'xxxxxxx#gmail.com'],
name: req.body.firstName,
code: req.body.workshopCode,
subject: req.body.workshopTitle,
text: req.body.messageBody
};
mailgun.messages().send(data, function(error, body) {
console.log(body);
});
});
Both From and To email are valid email address and the req.body displayed in the console.log is the correct data passed from the Front-End.
This the Front-end sendmail module:
sendEmailConfirmation(message) {
const headers = new HttpHeaders();
headers.append('Content-Type', 'application/x-www-form-urlencoded');
this.http.post('http://localhost:3000/users/sendMail', message).subscribe((data) => {
console.log('Mail return data: ' + data); >>>THIS LINE IS NEVER EXECUTED<<<<<<
},
);
}
What am I missing here??.....
I noticed the absence of a res.send so I added the following:
if (error) throw error;
else {
res.send('Email sent');
}
Now I get a forbidden error on the back-end:
(node:10780) UnhandledPromiseRejectionWarning: Error: Forbidden
at IncomingMessage.res.on (C:\connect2Server\node_modules\mailgun-js\lib\request.js:301:17)
at IncomingMessage.emit (events.js:165:20)
at endReadableNT (_stream_readable.js:1101:12)
at process._tickCallback (internal/process/next_tick.js:152:19)
(node:10780) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a ca
tch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 2)
Please make sure that if you are sending from Europe to also include the host like this:
const mg = mailgun({apiKey: MAILGUN_PRIVATE_API_KEY, domain: MAILGUN_DOMAIN_NAME, host:MAILGUN_HOST});
For Europe, host is api.eu.mailgun.net and for America it's api.mailgun.net from what I know.
Problem solved.....caused by typo in mailgun credentials. The '<' '>' brackets where copied over .....the clue was the Forbidden error.

Elastic search gives Bad request for ping

Code in elasticsearch.js file
function es() {
throw new Error('Looks like you are expecting the previous "elasticsearch" module. ' +
'It is now the "es" module. To create a client with this module use ' +
'`new es.Client(params)`.');
}
es.Client = require('./lib/client');
es.ConnectionPool = require('./lib/connection_pool');
es.Transport = require('./lib/transport');
es.errors = require('./lib/errors');
module.exports = es;
var elasticsearch = require('elasticsearch')
var client = new es.Client({
host: 'localhost:9200',
log: 'trace',
})
// Ping the cluster
client.ping({
requestTimeOut: 30000,
},
function(error){
if(error) {
console.log(error)
console.error("elasticsearch cluster is down!")
}
else {
console.log("All is well")
}
})
and I am running elastic search locally with command $bin/elasticsearch
but when I do $node elasticsearch.js it gives the error saying
Elasticsearch INFO: 2018-01-22T11:17:50Z
Adding connection to http://localhost:9200/
Elasticsearch DEBUG: 2018-01-22T11:17:50Z
starting request {
"method": "HEAD",
"requestTimeout": 3000,
"castExists": true,
"path": "/",
"query": {
"requestTimeOut": 30000
}
}
Elasticsearch TRACE: 2018-01-22T11:17:50Z
-> HEAD http://localhost:9200/?requestTimeOut=30000
<- 400
Elasticsearch DEBUG: 2018-01-22T11:17:50Z
Request complete
{ Error: Bad Request
at respond (/Users/ElasticSearchServer/node_modules/elasticsearch/src/lib/transport.js:307:15)
at checkRespForFailure (/Users/ElasticSearchServer/node_modules/elasticsearch/src/lib/transport.js:266:7)
at HttpConnector.<anonymous> (/Users/ElasticSearchServer/node_modules/elasticsearch/src/lib/connectors/http.js:159:7)
at IncomingMessage.bound (/Users/ElasticSearchServer/node_modules/elasticsearch/node_modules/lodash/dist/lodash.js:729:21)
at emitNone (events.js:91:20)
at IncomingMessage.emit (events.js:185:7)
at endReadableNT (_stream_readable.js:974:12)
at _combinedTickCallback (internal/process/next_tick.js:80:11)
at process._tickCallback (internal/process/next_tick.js:104:9)
status: 400,
displayName: 'BadRequest',
message: 'Bad Request',
path: '/',
query: { requestTimeOut: 30000 },
body: undefined,
statusCode: 400,
response: '',
toString: [Function],
toJSON: [Function] }
elasticsearch cluster is down!
If I try adding new index, delete index, check the health or search, it works fine and gives the appropriate result.
Can anyone help me to fix the issue? thanks in advance!
In the new JavaScript client every option that is not intended for Elasticsearch lives in a second object, your code should be updated as follows:
'use strict'
const { Client } = require('#elastic/elasticsearch')
const client = new Client({ node: 'http://localhost:9200' })
client.ping({}, { requestTimeout: 20000 }, (err, response) => {
...
})
In the response object other than body, statusCode, and headers, you will also find a warnings array and a meta object, which should help you debug issues.
In this case, warnings contained the following message: 'Client - Unknown parameter: "requestTimeout", sending it as query parameter'.

Resources