Propagate ApolloError to client - javascript

I have real hard time to get custom Apollo error on the client side.
Here is the server code:
...
const schema = makeExecutableSchema({
typeDefs: [constraintDirectiveTypeDefs, ...typeDefs],
resolvers,
schemaTransforms: [constraintDirective()],
});
const server = new ApolloServer({
schema,
dataSources,
context({ req }) {
const token = req.headers.authorization;
const user = token ? getUserFromToken(token) : '';
return { user };
},
debug: false,
formatError: (err) => {
// ToDo: Generate unique token and log error
if (err!.extensions!.code == 'INTERNAL_SERVER_ERROR') {
return new ApolloError('We are having some trouble', 'ERROR', {
token: 'uniquetoken',
});
}
return err;
},
uploads: false,
});
...
Client code:
...
const ADD_CLAIM = gql`
mutation addClaim($claim: ClaimInput!) {
addClaim(claim: $claim) {
id
}
}
`;
...
const [addClaim, { data, error }] = useMutation(ADD_CLAIM);
...
const onSubmit = async () => {
try {
debugger;
const r = await addClaim({
variables: {
input: {
id: insured.insured,
date: '20/12/2020',
...
therapy: treatment.treatments.map(treat => ({
id: treat.treatId,
...
})),
},
},
});
debugger;
console.log('r', r);
} catch (err) {
debugger;
setFormError(error ? error.message : err.message);
console.log('Error:', err);
}
};
...
if (error) {
debugger;
return <div>error</div>;
}
I expect to get the custom error : "We are having some trouble".
However, no matter what I do I got: "Response not successful: Received status code 400"
I am 100% give custom error from the server:
But I receive on client side:
Moreover, when I check network tab of Developer Tools, response I do have my error:
But I cannot access it from the code.
BTW, in the playground I see my error:

Here where are my errors :
error.networkError.result.errors
What nobody knows ?
Or
const errorLink = onError(({ graphQLErrors, networkError }) => {
debugger;
console.log(graphQLErrors);
console.log(networkError);
});
const client = new ApolloClient({
...
link: ApolloLink.from( [errorLink, ...]),
});
It works as well.
Yes, sometimes GraphQL is a nasty beast

Related

UnhandledPromiseRejectionWarning: ValidationError:

I am a student I am trying to create a booking api and I need to authenticate if the user that log in is an admin that is allowed to add courses and if not an admin would return access denied..
module.exports.addCourse = (reqBody) => {
let newCourse = new Course({
name : reqBody.name,
description : reqBody.description,
price : reqBody.price
});
return newCourse.save().then((course,error) => {
if (!req.user.isAdmin) {
return res.status(401).send({ message: "Access denied" });;
} else if (reqBody.isAdmin = true){
return true;
};
});
};
router.post("/",auth.verify,(req,res)=>{
const data ={
course: req.body,
isAdmin: auth.decode(req.headers.authorization).isAdmin
}
courseController.addCourse(data).then(resultFromController=>res.send(resultFromController));
});
module.exports = router;
I wanted to add the course in my database if the user is an admin and does not allow if not an admin, I always get the error:
(node:17308) UnhandledPromiseRejectionWarning: ValidationError: Course validation failed: name: Course is required, description: Description is required, price: Price is required
even though I have given the name description and price and also added the token in the postman.
why are you using callback and promises? your syntax is not right...
// Alter 1
module.exports.addCourse = (reqBody) => {
const newCourse = new Course({
name : reqBody.name,
description : reqBody.description,
price : reqBody.price
});
return newCourse.save()
.then((course) => {
// ...code
}).catch((err) => {
// code error
})
}
router.post("/",auth.verify,(req,res)=>{
const data ={
course: req.body,
isAdmin: auth.decode(req.headers.authorization).isAdmin
}
courseController.addCourse(data).then(resultFromController=>res.send(resultFromController));
});
module.exports = router;
// alter 2
module.exports.addCourse = async (reqBody) => {
const newCourse = new Course({
name : reqBody.name,
description : reqBody.description,
price : reqBody.price
});
return await newCourse.save()
}
router.post("/",auth.verify, async (req,res)=>{
const data ={
course: req.body,
isAdmin: auth.decode(req.headers.authorization).isAdmin
}
try {
const response = await courseController.addCourse(data)
// response
} catch (err) {
// error response
}
});
module.exports = router;
// alter 3
module.exports.addCourse = (reqBody) => {
const newCourse = new Course({
name : reqBody.name,
description : reqBody.description,
price : reqBody.price
});
return new Promise((resolve, reject) => {
newCourse.save()
.then((course) => {
resolve(course)
}).catch((err) => {
reject(err)
})
})
}
router.post("/",auth.verify,(req,res)=>{
const data ={
course: req.body,
isAdmin: auth.decode(req.headers.authorization).isAdmin
}
return new Promise((resolve, reject) => {
courseController.addCourse(data)
.then((resultFromController) => {
resolve(res.send(resultFromController))
}).catch((error) => {
reject(res.send('error'))
})
})
});

Catch error with try catch block and display

Error handling middleware in node js backend:
app.use((error, req, res, next) => {
console.log(error);
const status = error.statusCode || 500;
const message = error.message;
const data = error.data;
res.status(status).json({ message: message, data: data });
});
I have the following try catch block from my app:
userLogin() {
//axios vue instance
this.$http
.post("/auth/signup", this.formData)
.then((res) => {
// res.status == 201 ? (this.formData = {}) : "";
console.log(res);
})
.catch((err) => {
console.log(err);
console.log(err.data.data);
console.log(err.data.msg);
});
},
The output of the above catch block are as follows:
[![enter image description here][2]][2]
While my rest api sends the message in following format (seen on Network > Preview)
{
message: "Validation failed.",
…
}
data: [{
location: "body",
param: "email",
value: "test#test.com",
msg: "E-Mail address already exists!"
}]
message: "Validation failed."
I want to access the data array and print its content.
How can I access data ?
We can catch custom error message:
userLogin() {
this.$http
.post("/auth/signup", this.formData)
.then((res) => {
// res.status == 201 ? (this.formData = {}) : "";
console.log(res);
})
.catch((err) => {
console.log(err.response.data.data);
});
},
Axios stores the actual response in its data prop (unrelated to your API's data property in the response contents), so you could access your data property like this:
this.$http.post('/auth/signup', this.formData)
.then((res) => {
const apiResponse = res.data
console.log(apiResponse.data) // logs `data` property from API response contents
})
.catch((err) => {
/*...*/
})
More readable format
async userLogin() {
try {
// Call the server
const {data} = await this.$http.post("/auth/signup", this.formData);
// If you want message
let message = data.message;
// If you want the data object, ex data: [{location: "body"....}]
let dataObject = data.data;
} catch (e) {
// In case of error
console.log(err.response);
}
};

Error "Assignment to constant variable" in ReactJS

I did follow a tutorial of how to integrate mailchimp with node backend. I have never touched back end, so am pretty lame at it.
When I POST to their API I get the subscriber's credentials, but I get an error back - "Assignment to constant variable". Reading through the web and other SO questions, it seems like I am trying to reassign a CONST value.
I had a goooooooooood look at my code and the only thing I have noticed that might be issues here is
request(options, (error, response, body) => {
try {
const resObj = {};
if (response.statusCode == 200) {
resObj = {
success: `Subscibed using ${email}`,
message: JSON.parse(response.body),
};
} else {
resObj = {
error: ` Error trying to subscribe ${email}. Please, try again`,
message: JSON.parse(response.body),
};
}
res.send(respObj);
} catch (err) {
const respErrorObj = {
error: " There was an error with your request",
message: err.message,
};
res.send(respErrorObj);
}
});
I have noticed I am creating an empty object called "resObj", then trying to assign a value to it.
I have tried changing the CONST to LET, but I get an error saying: "resObj is not defined".
Here is my front end code:
import React, { useState } from "react";
import "./App.css";
import Subscribe from "./components/Subscribe";
import Loading from "./components/Loading/Loading";
import axios from "axios";
import apiUrl from "./helpers/apiUrl";
function App() {
const [loading, setLoading] = useState(false);
const [email, setEmail] = useState("");
const handleSendEmail = (e) => {
setLoading(true);
console.log(email);
axios
.post(`${apiUrl}/subscribe`, { email: email })
.then((res) => {
if (res.data.success) {
alert(`You have successfully subscribed!, ${res.data.success}`);
setEmail("");
setLoading(false);
} else {
alert(`Unable to subscribe, ${res.data.error}`);
console.log(res);
setLoading(false);
setEmail("");
}
})
.catch((err) => {
setLoading(false);
alert("Oops, something went wrong...");
console.log(err);
setEmail("");
});
e.preventDefault();
};
const handleInput = (event) => {
setEmail(event.target.value);
};
// const handleLoadingState = (isLoading) => {
// setLoading({ isLoading: loading });
// console.log(loading);
// };
return (
<div className='App'>
<h1>Subscribe for offers and discounts</h1>
{loading ? (
<Loading message='Working on it...' />
) : (
<Subscribe
buttonText='Subscribe'
value={email}
handleOnChange={handleInput}
handleOnSubmit={handleSendEmail}
/>
)}
</div>
);
}
export default App;
And the Back end code:
const restify = require("restify");
const server = restify.createServer();
const corsMiddleware = require("restify-cors-middleware");
const request = require("request");
require("dotenv").config({ path: __dirname + "/variables.env" });
const subscribe = (req, res, next) => {
const email = req.body.email;
const dataCenter = process.env.DATA_CENTER;
const apiKey = process.env.MAILCHIMP_API_KEY;
const listID = process.env.LIST_ID;
const options = {
url: `https://${dataCenter}.api.mailchimp.com/3.0/lists/${listID}/members`,
method: "POST",
headers: {
"content-type": "application/json",
Authorization: `apikey ${apiKey}`,
},
body: JSON.stringify({ email_address: email, status: "subscribed" }),
};
request(options, (error, response, body) => {
try {
const resObj = {};
if (response.statusCode == 200) {
resObj = {
success: `Subscibed using ${email}`,
message: JSON.parse(response.body),
};
} else {
resObj = {
error: ` Error trying to subscribe ${email}. Please, try again`,
message: JSON.parse(response.body),
};
}
res.send(respObj);
} catch (err) {
const respErrorObj = {
error: " There was an error with your request",
message: err.message,
};
res.send(respErrorObj);
}
});
next();
};
const cors = corsMiddleware({
origins: ["http://localhost:3001"],
});
server.pre(cors.preflight);
server.use(restify.plugins.bodyParser());
server.use(cors.actual);
server.post("/subscribe", subscribe);
server.listen(8080, () => {
console.log("%s listening at %s", server.name, server.url);
});
If anyone could help I would be very grateful. The subscription form works, but I need to clear that bug in order for my front end to work correctly onto submission of the form.
Maybe what you are looking for is Object.assign(resObj, { whatyouwant: value} )
This way you do not reassign resObj reference (which cannot be reassigned since resObj is const), but just change its properties.
Reference at MDN website
Edit: moreover, instead of res.send(respObj) you should write res.send(resObj), it's just a typo

Firebase functions.https.onCall returns null

I have the following firebase cloud function written in node.js that I call from my Android app.The function works fine but I get null when trying to get results in Android. I need return to my android app console.log(result.data); line, but that line called after firebase function completely done, so my Android app gets null.
index.js
'use strict'
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const subcheck = require('./subcheck');
admin.initializeApp();
exports.subcheck = functions.https.onCall((data, context) => {
subcheck.verifySubscription(data, context);
});
And this is my subcheck.js
'use strict'
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const key = require('./service-account-key.json'); // JSON key file
const {google} = require('googleapis');
const authClient = new google.auth.JWT({
email: key.client_email,
key: key.private_key,
scopes: ["https://www.googleapis.com/auth/androidpublisher"]
});
const playDeveloperApiClient = google.androidpublisher({
version: 'v3',
auth: authClient
});
//admin.initializeApp(functions.config().firebase);
exports.verifySubscription = function(data, context) {
const skuId = data.sku_id;
const purchaseToken = data.purchase_token;
const packageName = data.package_name;
return authClient.authorize()
.then(function(result) {
return playDeveloperApiClient.purchases.subscriptions.get({
packageName: packageName,
subscriptionId: skuId,
token: purchaseToken
}).then(function(result) {
if (result.status === 200) {
console.log(result.data);
return {
data: result.data,
status: 200,
message: "Verified Subscription"
};
} else {
console.log("Failed to verify subscription, Try again!");
return {
data: result.data,
status: 500,
message: "Failed to verify subscription, Try again!"
};
}
})
.catch(function(error) {
console.log(error);
});
})
.catch(function(error) {
console.log(error);
});
}
This is my firebase functions console log:
I'm trying to get results in Android with the following method :
private Task<String> checkUserSubscribed(PurchaseHistoryRecord purchase) {
Map<String, Object> mapUserPurchase = new HashMap<>();
mapUserPurchase.put("sku_id", purchase.getSku());
mapUserPurchase.put("purchase_token", purchase.getPurchaseToken());
mapUserPurchase.put("package_name", "xxxxxxx");
return mFirebaseFunctions
.getHttpsCallable("subcheck")
.call(mapUserPurchase)
.continueWith(new Continuation<HttpsCallableResult, String>() {
#Override
public String then(#NonNull Task<HttpsCallableResult> task) throws Exception {
String result = (String) task.getResult().getData();
return result;
}
}).addOnCompleteListener(new OnCompleteListener<String>() {
#Override
public void onComplete(#NonNull Task<String> task) {
String result = task.getResult();
Toast.makeText(MainActivity.this, "func results: " + result, Toast.LENGTH_SHORT).show();
}
});
}
The problem most probably comes from the fact that you don't return the result of the call to the verifySubscription() function in:
exports.subcheck = functions.https.onCall((data, context) => {
subcheck.verifySubscription(data, context);
});
You should do:
exports.subcheck = functions.https.onCall((data, context) => {
return subcheck.verifySubscription(data, context);
});
Also, you should probably re-organize your Promise chaining as follows, to avoid nesting the calls to then(), see more details/explanantions here.
exports.verifySubscription = function(data, context) {
const skuId = data.sku_id;
const purchaseToken = data.purchase_token;
const packageName = data.package_name;
return authClient.authorize()
.then(result => {
return playDeveloperApiClient.purchases.subscriptions.get({
packageName: packageName,
subscriptionId: skuId,
token: purchaseToken
})
})
.then(result => {
if (result.status === 200) {
console.log(result.data);
return {
data: result.data,
status: 200,
message: "Verified Subscription"
};
} else {
console.log("Failed to verify subscription, Try again!");
return {
data: result.data,
status: 500,
message: "Failed to verify subscription, Try again!"
};
}
})
})
.catch(function(error) {
console.log(error);
});
}
Finally, you may have a look at the documentation on error handling in a Callable Cloud Function. Instead of returning {data: result.data,status: 500, ...}; you may throw an instance of functions.https.HttpsError.

cannot read property of when trying to send data from React to Express

I'm trying to send some data from a React form to my Express back end. To do this I'm using fetch where I'm trying to send some variable data from react. I'm console logging the data before running the fetch to see if it is there, console log can see the data.
My error states
[0] (node:2966) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'message' of undefined
So it seems like my Express back end can't see the variable data.
How I'm sending the data from react
handleSubmit = async e => {
e.preventDefault();
console.log("Submit was pressed!");
if (this.state.email === "") {
}
const { name } = this.state;
const query = this.state.query;
const subject = "kontakt fra nettside";
const message = { name, query };
console.log(message.name, message.text, "data is");
fetch(
"http://localhost:5000/api/email", variabler
{
method: "POST",
cache: "no-cache",
headers: {
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Credentials": true,
content_type: "application/json"
},
body: JSON.stringify(message, subject)
}
); //.then(response => response.json());
};
My file for retrieving the data from the front end in Express
const emailConfig = require("./emailConfig")();
const mailgun = require("mailgun-js")(emailConfig);
exports.sendEmail = (recipient, message, attachment) =>
new Promise((resolve, reject) => {
const data = {
from: "Test <test#test.no>", // Real email removed from this post
to: recipient,
subject: message.subject,
text: message.query,
inline: attachment,
html: message.html
};
mailgun.messages().send(data, error => {
if (error) {
return reject(error);
}
return resolve();
});
});
and sendMail.js
const express = require("express");
const sendMail = express.Router();
const emailUtil = require("./emailUtil");
const { sendEmail } = emailUtil;
sendMail.post("/", async (req, res, next) => {
// const { recipient, message } = req.body;
console.log("Request mottatt");
const recipient = "test#test.no";
const message = req.body.message;
try {
await sendEmail(recipient, message);
res.json({ message: "Your query has been sent" });
console.log("Message has been sent");
await next();
} catch (e) {
await next(e);
console.log("nah", e);
}
});
module.exports = sendMail;
I can't figure out where the error is, any ideas? :)

Categories