Store GraphQL errors as String - javascript

I have a login form. When the submit button is hit, I check via the GraphQL backend if email and password are correct. If yes, a token is returned and stored in local storage. At times, there are errors like:
'Incorrect Password' or 'User Doesn't Exist'.
Is there any way to store these errors as strings so I can display them later using conditional rendering?
This is how my mutation looks like:
function submitForm(LoginMutation: any) {
const { email, password } = state;
if(email && password){
LoginMutation({
variables: {
email: email,
password: password,
},
}).then(({ data }: any) => {
localStorage.setItem('token', data.loginEmail.accessToken);
})
.catch(console.log)
}
}
and I am using it like this in my return
return (
<Mutation mutation={LoginMutation}>
{(LoginMutation: any) => (
....)}>
</Mutation>
)
For now, I am just displaying a single error on the basis of whether the token exists or not but I want to make my error specific to the GraphQL errors.
function ShowError(){
if (!localStorage.getItem('token'))
{
console.log('Login Not Successful');
return <Typography color='primary'>Login Not Successful</Typography>
}
}
Edit:
Example Error:
[Log] Error: GraphQL error: Key (email)=(c#c.com) already exists.
I tried this but it never logs anything:
.then(({data, errors}:any) => {
if (errors && errors.length) {
console.log('Errors', errors);
setErrorMessage(errors[0].message);
console.log('Whats the error', errors[0].message)
} else {
console.log('ID: ', data.createUser.id);
}
})
```
The backend isn't made by me

It depends on how you have a few things set up, but, assuming you have access to state in your ShowError function:
When using GraphQL, errors can happen in 2 ways:
1. A network error, which will be caught in the .catch. To handle this, in your catch you can store the error message in state, and then access it from ShowError:
...
.catch(err => {
setState({errorMessage: err.message});
});
As a result of a bad query, which generally returns a successful response with an errors array. To handle this case, you can add an error check in your .then:
...
.then(({data, errors}) => {
if (errors && errors.length) {
setState({errorMessage: errors[0].message});
} else {
localStorage.setItem('token', data.loginEmail.accessToken);
}
});

Related

Printing firebase errors on component from vuex store, (getting errors in order to be translated later with i18n)

thank you in advance for reading me. So I'm new to vuex and after configuring my store, now I want to get my firebase errors displayed on my components, I wanted to catch them via specific code error, so I can translated to 3 different languages later, so far I had no luck. So my store declarations looks something like this, I put as example the login action where I'm trying to get the errors into a state. The error part is more like what I want to achieve, is not clear if I need to pass it as payload .
//My sample vuex action for login
const loginAction = (context, payload) => {
firebase
.auth()
.signInWithEmailAndPassword(payload.email, payload.password)
.then((userCredential) => {
context.commit('logIn', userCredential.user.uid);
console.log('userCredential', userCredential)
router.push('/');
context.dispatch('retrieveDbValues');
return true;
})
.catch(error => {
switch (error.code) {
case 'auth/invalid-email':
error.message = 'The email is invalid'
break
case 'auth/user-not-found':
error.message = 'No account with that email was found'
break
case 'auth/wrong-password':
error.message = 'The password is incorrect'
break
default:
error.message = 'Email or password was incorrect'
break
}
});
}
//My current mutation for this error
setError(state, payload) {
state.error = payload;
},
//My state with the error value
export default{
state:{
error: null
}
}
//My sample with getterns for error
getters: {
error(state) {
return state.error;
}
}
Now in my login component, I have something like this inside my form declaration at the top
<form id="signup-form" #submit.prevent="checkForm">
<p v-if="error"> {{ error }} </p>
...
</form>
And in my computed methods from my login component I have something like
computed:{
error(){
return this.$store.getters.error;
}
}
In that catch error above, in the first block of code, it is more like what I want to achieve. I also tried the simple version just printing the error without the code(see example below), but also did not work.
.catch(error => {
context.commit('setError', error.message);
});
When testing, meaning I just submit the email and I intentionally write a wrong password, I don't get the error printed, just the error in the console, "server respond with status 400" which i think is correct, because couldn't connect but not error displayed in my component. Any idea?
Solved so it seems was needed a small twick on the vuex action, because I was not entering to the catch for some reason, so I asked if (credentials) on the then:
const loginAction = (context, payload) => {
firebase
.auth()
.signInWithEmailAndPassword(payload.email, payload.password)
.then((userCredential) => {
if (userCredential) {
context.commit('logIn', userCredential.user.uid);
router.push('/');
context.dispatch('retrieveDbValues');
return true;
}
return false;
})
.catch(error => {
context.commit('setError', firebaseErrors[error.code] || error.message);
console.log(error.code);
});
}
And in the catch error, I'm passing a constant that have the error codes from firebase, so I have them setup to be translated in the vue store. And all of this is declared in my same vuex store.
const firebaseErrors = {
'auth/user-not-found': 'No user corresponding to this email or have a typo',// No user corresponding to this email or have a typo
'auth/invalid-email':'The email is not valid',
'auth/wrong-password':'The password is incorrect',
};
And for me this is working, hope it helps some else

How to prevent Firebase Cloud Function from crashing and how to send error message as response?

I've created a simple createUser function which is executed on call. I have one problem though. The function is crashing when the user is trying to register with an already existing email. I mean, it's ok, since no one wants to have 2 users with the same email address but I want to prevent crushing function, instead, I want to send an error message as a response.
export const createUserTest = functions.https.onCall((data, context) => {
const {email, password} = data;
return new Promise((resolve, reject)=>{
try{
admin
.auth()
.createUser({
email: email,
emailVerified: false,
password: password,
disabled: false,
})
.then((user) => {
resolve({
result: 'success',
user: user,
}) ;
})
.catch((error) => {
reject(error) ;
});
}catch(error) {
reject (error)
}
})
});
I tried to put the function in to try/catch block but it didn't help. Do you have an idea of how I can achieve my goal?
As explained in the doc for Callable Cloud Functions, "to ensure the client gets useful error details, return errors from a callable by throwing (or returning a Promise rejected with) an instance of functions.https.HttpsError".
The error has a code attribute that can be one of the values listed here. In your case, the most appropriate seems to be already-exists.
On, the other hand, you'll find here the Admin SDK Authentication errors list and you'll see that in case the provided email is already in use by an existing user the error code is auth/email-already-exists.
So you can adapt your code as follows:
export const createUserTest = functions.https.onCall((data, context) => {
const { email, password } = data;
return admin
.auth()
.createUser({
email: email,
emailVerified: false,
password: password,
disabled: false,
})
.then((user) => {
return {
result: 'success',
user: user,
}
})
.catch((error) => {
if (error.code === 'auth/email-already-exists') {
throw new functions.https.HttpsError('already-exists', 'The provided email is already in use by an existing user');
} else {
throw new functions.https.HttpsError('...other code....', '...');
// If an error other than HttpsError is thrown, your client instead receives an error with the message INTERNAL and the code internal.
}
});
});
See here in the doc, how to handle errors on the client side. If error.code == 'already-exists' you know that it's because the email is already in use.

"Cannot read property message of null" when reading error status code from http response in Angular 8

I'm developing in Angular 8 and issuing a http post request to a .net core web api and returning a 400 status code if username or password is incorrect. Chrome console says 400 returned but when extracting the status code in the returned observable from the http request response, I get a Cannot read property message of null error message. How can I fix this? Thanks.
Login Component:
this.authService.login(
{
username: this.f.username.value,
password: this.f.password.value
}
)
.subscribe(
res => {
if(this.returnUrl != null){
this.router.navigate([this.returnUrl]);
}
else {
let role = res.role[0];
this.router.navigate([`${role}`]);
}
},
error => {
//This line throws the error. the value of error is "cannot read message property of null" and error.status = undefined.
alert(error.status);
this.badCredentials = true;
this.router.navigate(['/login']);
});
Auth Service:
login(user: {username: string, password: string}) :Observable<any>{
return this.http.post<any>(`${applicationPaths.loginApiUrl}`, user)
.pipe(
tap(response => this.doLoginUser(response)),
catchError((error): any => {
return throwError(`Connection Error: ${error}`);
}
));
}
UPDATE:
I updated my code to the following in my angular application and it still returns the same error message: Server returned code: undefined, error message is: Cannot read property 'message' of null
login(user: {username: string, password: string}) :Observable<any>{
return this.http.post<any>(`${applicationPaths.loginApiUrl}`, user)
.pipe(
tap(response => this.doLoginUser(response)),
catchError(this.handleError));
}
handleError(err: HttpErrorResponse) {
let errorMessage = '';
if(err.error instanceof ErrorEvent){
//a client-side or network error occured. Handle it accordingly.
errorMessage = `An error occured: ${err.error.message}`;
} else {
//The back-end returned an unsuccessful response code.
errorMessage = `Server returned code: ${err.status}, error message is: ${err.message}`;
}
console.error(errorMessage);
return throwError(errorMessage);
}
But when I do return BadRequest("incorrect username or password."); or return BadRequest(); it returns the error message Server returned code: undefined, error message is: undefined. So maybe this has to do with the way I'm returning the error code from the web api in the back end. I'm not sure what needs to be fixed there.
status is only provided if you observe: 'response'
try editting your authService like this
login(user: {username: string, password: string}) :Observable<any>{
return this.http.post<any>(`${applicationPaths.loginApiUrl}`, user
// NEW CODE HERE
{ observe: 'response' }
)
.pipe(
tap(response => this.doLoginUser(response)),
catchError((error): any => {
return throwError(`Connection Error: ${error}`);
}
));
}
Add { observe: 'response' } to your code like this
login(user: {username: string, password: string}) :Observable<any>{
return this.http.post<any>(`${applicationPaths.loginApiUrl}`, user, { observe: 'response' })
.pipe(
tap(response => this.doLoginUser(response)),
catchError((error): any => {
return throwError(`Connection Error: ${error}`);
}
));
}
Then try to access your errror data in your catchError like this
error.statusText
error.statusCode
Edit you should use this code in your controller
return BadRequest();
Your code
return StatusCode(400);
only return status code
In your .NET API: return BadRequest("Incorrect username or password");
In your Angular app:
catchError((error): any => {
return throwError(`Connection Error: ${error.error}`);
}
));
In my case I received this because I'd incorrectly annotated my API method as a HttpGet and not a HttpPost!
The message doesn't really seem to reflect this however when I fixed the API it worked.
have a look at your error.intercepter.ts and change it as
[ const error = err.error.message || err.statusText; ]
luck

How to handle multiple errors in promise chain?

I'm using AWS Amplify for authentication and Stripe for the payment to create sign up page.
PROBLEM: I can't find a way to combine validations for Email and password section(from AWS Amplify) with payment info section(from Stripe).
My current code creates a Stripe token and call API(with valid payment info) then handles the error message from userSignupRequest which takes care of email and password fields.
How do I validate the email and password with payment info then create account in AWS and Stripe?
// Stripe payment process
this.props.stripe.createToken(
{
email: this.state.email
}
).then(result => {
// PROBLEM: Form server validation from Stripe
if(result.error){
return this.setState({ errors: { errorMsg: result.error.message }, isLoading: false })
}
// if success, create customer and subscription with result.token.id
const apiName = 'NameOfAPI';
const path = '/stripe/signup';
let myInit = {
body: {
"stripeToken": result.token.id,
"email": this.state.email
}
}
API.post(apiName , path, myInit).then(reponse => {
this.props.userSignupRequest(this.state.email, this.state.password, reponse).then(user => {
this.setState({
confirmAccount: true,
isLoading: false,
userEmail: this.state.email,
errors: {}
})
this.props.history.push('/signup#confirm-account')
}).catch(err => {
// PROBLEM: Form server validation
this.setState({ errors: { errorMsg: err.message }, isLoading: false })
})
}).catch(err => {
console.log(err)
this.setState({ errors: { errorMsg: err }, isLoading: false })
});
})
It seems like we have a very similar stack. My solution was to handle everything server-side. You'll need to give your lambda functions the appropriate IAM permissions to access Cognito. The code below is a little long. I use async/await, which really cleans things up for me. You'll need to use Lambda with node 8 to use async/await though.
I validate that everything matches the right format client-side (i.e. emails are really emails, passwords are the right length). I realized the only error that could come up is an "existing user" error from Cognito. The idea is: test if the user exists before you attempt to sign the person up with Stripe. There's no way to "test" if the user's credit card is valid with Stripe. It's all or nothing. If it's valid it will go through, if not, you'll get an error. If it goes through, you can then sign up the user with Cognito, knowing you should not get an error (you've validated the email and password client-side and, you know the use doesn't already exist).
For reference, here's the aws-sdk for cognito.
const AWS = require('aws-sdk');
const cognito = new AWS.CognitoIdentityServiceProvider({
region: "region",
userPoolId: "cognito_user_pool_id",
});
module.exports.signUpUser = (payload) => {
const usernamePayload = {
UserPoolId: "cognito_user_pool_id",
Username: payload.email,
};
// I use emails for usernames.
new Promise((resolve, reject) => {
cognito.adminGetUser(usernamePayload, (error, response) => {
if (error && error.code === 'UserNotFoundException') {
resolve(false);
} else if (error) {
reject(error);
} else {
// if adminGetUser doesn't fail, it means the username exists
resolve(true);
}
});
}).then((usernameExists) => {
if (!usernameExists) {
// run stripe API stuff
// always run before sign up below to catch stripe errors
// and return those errors to client
// before you sign up the user to Cognito
// since you've already verified the user does not exist
// it would be rare for an error to come up here
// as long as you validate passwords and emails client-side
const signUpPayload = {
ClientId: "cognito_user_pool_client_id",
Username: payload.email,
Password: payload.password,
UserAttributes: [
{
Name: 'email',
Value: payload.email,
},
],
};
new Promise((resolve, reject) => {
cognito.signUp(signUpPayload, (error, response) => {
if (error) {
reject(error);
} else {
resolve(response);
}
});
}).catch((error) => {
// you should hopefully encounter no errors here
// once you get everything setup correctly
console.log(error);
})
} else {
// means username already exists, send error to client
// saying username exists
}
}).catch((error) => {
// may want to dispatch this error to client
console.log(error);
});
return null;
};

ReactJS + Redux: Why isn't MongoDB saving data to the database even with correct API requests?

I have a MongoDB/Webpack/NodeJS Express set up in my ReactJS + Redux project.
I am making API calls from action creators in redux, and reach the API server and get a successful status back, yet the data never gets saved and the database never gets created even checking with in terminal mongo -> dbs and it doesn't show practicedb database which I named it as.
What could be the issue? Am I missing something?
Any guidance or insight would be greatly appreciated. Thank you
This is my set up for API:
import axios from 'axios';
import { browserHistory } from 'react-router';
import cookie from 'react-cookie';
import { AUTH_USER, AUTH_ERROR } from './types';
const API_URL = 'http://localhost:3000/api';
export function errorHandler(dispatch, error, type) {
let errorMessage = (error.data.error) ? error.data.error : error.data;
// NOT AUTHENTICATED ERROR
if(error.status === 401) {
errorMessage = 'You are not authorized to do this.';
}
dispatch({
type: type,
payload: errorMessage
});
}
export function registerUser({ email }) {
return function(dispatch) {
axios.post(`${API_URL}/auth/register`, { email })
.then(response => {
console.log('THIS IS TESTING PURPOSE')
console.log(response)
dispatch({ type: AUTH_USER });
})
.catch((error) => {
errorHandler(dispatch, error.response, AUTH_ERROR)
});
}
}
And my API controller is set up as such:
"use strict";
const User = require('../models/user')
exports.register = function(req, res, next) {
const email = req.body.email;
console.log('ERROR 1')
if(!email) {
return res.status(422).send({ error: 'You must enter an email address.'})
console.log('ERROR 1')
}
User.findOne({ email: email }, function(err, existingUser) {
if(err) { return next(err); }
console.log('ERROR 2')
if(existingUser) {
return res.status(422).send({ error: 'That email address is already in use.'})
}
console.log('ERROR 3')
let user = new User({
email: email,
})
console.log('ERROR 4')
user.save(function(err, user) {
if(err) { return next(err); }
console.log('ERROR 5')
res.status(201).json({
user: user,
})
})
})
console.log('ERROR 6')
}
Configuration for the API:
module.exports = {
'database': 'mongodb://localhost/practicedb',
'port': process.env.PORT || 3000,
'secret': 'dogcat',
}
The project so far just has an input text field, where it accepts an email address. If the email has already been registered, the API should return the error That email address is already in use. and it does.
So I tried console logging to see what the problem is, and the first time I submit the POST request, it logs the following (the terminal showing API console logs):
And if I try to submit the same email again, it throws me the API error that the email is already in use with 422 error, yet the data do not get saved and database (practicedb) never get created:
Also, what is the OPTIONS request that shows up in terminal? I only made an attempt to POST. Lastly, is OPTIONS why the ERROR log in API server is not logging in chronological order?
EDIT
You're using the wrong Mongo shell command: db will only show you the current database (test), but if you want to see a list of all databases, you should use show dbs.
If you want to switch databases:
use practicedb
And, if you want to see the collections in the current database:
show collections

Categories

Resources