Is my code the best way to use async await? - javascript

Am try to implement and learn async await functions in my login example, but I don't know if is the best, elegant and clean code. I have doubs meanly in catch errors, and if I need implement in a best way the const and functional programing. Can share your opinions?
app.post('/', async (req, res) => {
try {
const { email } = req.body.email; // destructuring
const usuarioEncontrado = await Usuario.findOne({email: email});
// Validate user exist
if (!usuarioEncontrado) { // when not exist return null
throw res.status(404).json({error: 'El usuario no existe'});
}
// validate user pass
if (!bcrypt.compareSync(req.body.password, usuarioEncontrado.password)) {
throw res.status(404).json({error: 'No match'});
}
const token = jwt.sign( // generate token
{
usuario: usuarioEncontrado
},
SEED,
{
expiresIn: (60 * 60)
}
);
res.status(200).json({ // send response
token: token,
usuario: usuarioEncontrado
});
} catch (e) { // send error
res.status(404).json(e);
}
}
THANKS

Your code shows a couple problems:
You're attempting to send double responses. First you do throw res.status(404).json(...). Then, you catch that exception and do res.status(404).json(e) again. That's not right. If you're going to send the response, then just return, don't throw. Or, just throw the exception without sending a response and send the actual error response from the catch handler.
Also, throw res.status(404).json({error: 'No match'}); sends the response and then throws whatever .json() returns which is probably not what you want. That won't be an error object of any kind.
I prefer to centralize the places I send an error response to one place in the request handler. That keeps you from ever attempting to send multiple responses and just makes the flow of the request handler easier to understand (in my opinion).
To do that, I just throw a custom error that may have a custom message/status associated with it and then catch all possible errors in one place. Here's one way to do that. The myError class can be used everywhere in your project, not specific to just one route. The idea is that often when you throw, you know in that context what you want the status and message to be so you set that in the custom Error object and can then use that info in the catch. The catch then has to determine whether it has your custom error or just a regular error. First, I have a reusable Error subclass that lets me throw, not only a message, but also a status value.
// reusable error class that contains a status in addition to the message
class MyError extends Error {
// this static method saves having to compare if it's a custom error object or not
// every time we use this
static sendError(res, e, status = 500) {
if (e instanceof MyError) {
e.sendError(res);
} else {
res.sendStatus(status);
}
}
constructor(msg, status = 500) {
// allow calling with or without new
if (!(this instanceof MyError)) {
return new MyError(msg, status);
}
super(msg);
this.status = status;
}
sendError(res) {
res.status(this.status).send(this.message);
}
}
And, then here's how you use that in your code and centralize the sending of the error status.
app.post('/', async (req, res) => {
try {
const { email } = req.body.email; // destructuring
const usuarioEncontrado = await Usuario.findOne({email: email});
// Validate user exist
if (!usuarioEncontrado) { // when not exist return null
throw MyError('El usuario no existe', 404);
}
// validate user pass
if (!bcrypt.compareSync(req.body.password, usuarioEncontrado.password)) {
throw MyError('No Match', 404);
}
const token = jwt.sign( // generate token
{
usuario: usuarioEncontrado
},
SEED,
{
expiresIn: (60 * 60)
}
);
res.status(200).json({ // send response
token: token,
usuario: usuarioEncontrado
});
} catch (e) { // log and send error response
// e may be either MyError or some other system generated Error
console.log(e);
MyError.sendError(res, e);
}
}

Related

Jest test that simulates throwing an Axios exception fails with empty error message

I am trying to write a Jest test to cover a scenario whereby an axios.post (in the code I am trying to test) throws and handles an exception. The test successfully throws an error for a mocked axios instance and the appropriate catch block in the code I am wishing to test is reached. However, the error/exception message is empty. This then causes some assertions that I am trying to do in the test to fail.
The relevant section of the code to test looks as follows:
try {
// Call the token endpoint with the client/user credentials and check the response.
const { status, data } = axios.post(authenticationConfig.tokenEndpoint,
'grant_type=client_credentials', { headers });
if (status === StatusCodes.OK) {
...
}
}
catch(err) {
console.log(JSON.stringify(err));
res.status(StatusCodes.UNAUTHORIZED);
res.json(err.response.data.error);
}
The corresponding test looks like:
it('cannot get access token', async () => {
const response = {
response: {
data: {
error: 'My error'
}
}
};
const req = {
headers: {
'authorization': 'Basic client_id:client_secret'
}
};
mockedAxios.mockImplementation(() => {
throw new Error(response);
});
const provide = await authenticationMiddleware.provide(req, res, next);
await provide(req, res, next);
expect(mockedAxios).toBeCalledTimes(1);
expect(res.status).toHaveBeenCalledTimes(1);
expect(res.status).toHaveBeenCalledWith(StatusCodes.UNAUTHORIZED);
});
The err object in the catch block is logged out as an empty object even though from the test I'm throwing an error with a fully populated object. The test passes if I remove the 'res.json' statement from the catch block.
● authentication middleware › cannot get access token
TypeError: Cannot read property 'data' of undefined
89 | console.log(JSON.stringify(err));
90 | res.status(StatusCodes.UNAUTHORIZED);
> 91 | res.json(err.response.data.error);
Any ideas most welcome please. No doubt the way that I'm mocking Axios and causing it to throw an exception is wrong. The code does enter the catch block but the 'err' object is empty for some reason.
Many thanks.

jwt.verify keeps on returning invalid signature despite working elsewhere

I have no idea why, but for some reason the jwt.verify function is complaining about an invalid signature in only part of my application. To give you some background, I am trying to set up auth headers using the context function with Apollo Server (which I guess is irrelevant anyway as the jwt.verify function should work in any javascript code snippet the same way) as follows:
context: ({ req }) => {
const token = req.get('Authorization') || '';
if (!token) {
console.log('no token detected. returning null...');
return { user: null };
}
return {
user: verifyUser(token.split(' ')[1]),
};
},
The verifyUser function:
const verifyUser = (token: string) => {
try {
return jwt.verify(token, process.env.JWT_SECRET as Secret);
} catch (error: any) {
console.error('error: ', error.message);
return null;
}
};
Note that I’m following this guide and have renamed getUser to verifyUser.
In each graphql request, I am providing a Bearer token, like so:
{
"Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwczovL2F3ZXNvbWVhcGkuY29tL2dyYXBocWwiOnsicm9sZXMiOlsiYWRtaW4iXSwicGVybWlzc2lvbnMiOlsicmVhZDphbnlfYWNjb3VudCIsInJlYWQ6b3duX2FjY291bnQiXX0sImlhdCI6MTU4NjkwMDI1MSwiZXhwIjoxNTg2OTg2NjUxLCJzdWIiOiIxMjM0NSJ9.31EOrcKYTsg4ro8511bV5nVEyztOBF_4Hqe0_P5lPps"
}
But every time I make a graphql request (a query or mutation) in the graphql playground, I am getting an invalid token message in the catch block, so presumably the jwt.verify function is failing. I wondered whether the JWT I provided to the Bearer code above is wrong, but I do not think it is. I am getting it from an authenticateUser resolver:
export const authenticateUser = async (
_: undefined,
{ input: { email, password } }: any
): Promise<any> => {
try {
const user = await User.findByEmail(email);
if (!user) {
throw new HttpError(404, 'User not found. Please create an account.');
}
const correctPassword = await bcrypt.compare(password, user.hashedPassword);
if (!correctPassword) {
throw new HttpError(403, 'Wrong password for this account.');
}
// assign object methods to the user instance as objects retrieved from db don't have methods
Object.setPrototypeOf(user, User.prototype);
const token = user.signToken(); // see below
return { token, id: user._id };
} catch (error: any) {
throw new HttpError(error.statusCode, error.message);
}
}
The user.signToken() function comes from a User class:
signToken(expiry: number | string = 60 * 24): string {
const secret = process.env.JWT_SECRET + this.hashedPassword;
// { ...this } overcomes error `Expected "payload" to be a plain object`
const token = jwt.sign({ ...this }, secret, {
expiresIn: expiry,
});
return token;
}
The only difference is that I am passing in a hashed password with the secret argument. I’ve also noticed that the error does not occur when using jwt.sign instead of jwt.verify (like in this post), but I assume I have to use the verify function.
Does anyone know why my code may not be working? The guide I am following does not pass a hashed password into the secret argument.

Can't use #Res() with FilesInterceptor()

I am trying to upload a file using builtin multer and after then sending the response back to the user for success or failure. It was all going good until today, when I try to upload the Response wont come. after digging a bit I find out that when i use #res with #UploadedFile it does not execute the controller. I am new to nest.js.
Working.
#Post('uploads/avatar')
async uploadFile(#Req() req, #UploadedFile() avatar) {
console.log(req.body);
if (!req.body.user_id) {
throw new Error('id params not found.');
}
try {
const resultUpload = await this.userService.uploadUserImage(
req.body.user_id,
avatar,
); // returns the url for the uploaded image
return resultUpload;
} catch (error) {
console.log(error);
return error;
}
}
Not Working.
#Post('uploads/avatar')
async uploadFile(#Req() req, #UploadedFile() avatar, #Res() res) {
console.log(req.body);
if (!req.body.user_id) {
throw new Error('id params not found.');
}
try {
const resultUpload = await this.userService.uploadUserImage(
req.body.user_id,
avatar,
); // returns the url for the uploaded image
return resultUpload;
res.send(resultUpload);
} catch (error) {
console.log(error);
res.send(error);
}
}
In nest, you should always avoid injecting #Res because then you lose a lot of things that make nest so great: interceptors, exception filters,...
And actually, in most cases you don't need #Res since nest will automatically handle sending the response correctly.
If you want to send data from a controller method, you can just return the data (Promises and Observables will be resolved automatically as well). If you want to send an error to the client, you can just throw the corresponding HttpException, e.g. 404 -> NotFoundException:
#Post('uploads/avatar')
async uploadFile(#Req() req, #UploadedFile() avatar) {
if (!req.body.user_id) {
// throw a 400
throw new BadRequestException('id params not found.');
}
try {
const resultUpload = await this.userService.uploadUserImage(
req.body.user_id,
avatar,
);
return resultUpload;
} catch (error) {
if (error.code === 'image_already_exists') {
// throw a 409
throw new ConflictException('image has already been uploaded');
} else {
// throw a 500
throw new InternalServerException();
}
}
}
If for some reason you have to inject #Res here, you cannot use the FilesInterceptor. Then you have to configure the multer middleware yourself.
Side note
You can create a custom decorator for accessing the userId:
import { createParamDecorator } from '#nestjs/common';
export const UserId = createParamDecorator((data, req) => {
if (!req.body || !req.body.user_id) {
throw new BadRequestException('No user id given.')
}
return req.body.user_id;
});
and then use it in your controller method like this:
#Post('uploads/avatar')
async uploadFile(#UserId() userId, #UploadedFile() avatar) {
look, when you are using an interceptor, you are handling (with using .handle()) the stream of response (observable) not a whole package of it, but using express #Res actually is somehow getting around the whole flow of response streaming.
this is also explicitly mentioned in nestjs official documents:
We already know that handle() returns an Observable. The stream
contains the value returned from the route handler, and thus we can
easily mutate it using RxJS's map() operator.
WARNING
The response mapping feature doesn't work with the
library-specific response strategy (using the #Res() object directly
is forbidden).

How to throw an error inside the pre handler in Hapi.js

I started using v17 of Hapi.js and I am running into issues when using the pre-handler.
I want to save a user into a database, but first I use the pre-handler to check if a user already exists. If the user exists, I want to throw an error. The structure of my route is as so...
module.exports = {
method: "POST",
path: "/users",
config: {
auth: false,
pre: [{ method: verify_unique_user}],
handler: create_user.create
}
}
The content of verify_unique_user is...
async function verify_unique_user(req, h) {
await User.findOne({
$or: [{email: req.payload.email}, {username: req.payload.username}]
},
(err, user) => {
if (user) {
// Check if username exists.
if (user.username === req.payload.username) {
throw Boom.badRequest("Username taken!");
}
// Check if email exists.
if (user.email === req.payload.email) {
throw Boom.badRequest("Email taken!");
}
}
});
return req;
}
Let's assume the user already exists in the database. Then an error will be thrown from either of the if statements. When this happens, I get the following error...
events.js:167
throw er; // Unhandled 'error' event
^
Error: Username taken!
at User.findOne (/Users/ericbolboa/Desktop/Warble/server/src/users/util/user_function.js:16:16)
This crashed my server. This is not what I want. If I throw an error in my handler function, the response looks like this...
{
"statusCode": 400,
"error": "Bad Request",
"message": "error"
}
But whenever I throw an error in the pre-handler, my server crashes. How can I throw errors properly?
Not sure if this is the source of the issue but you can simplify the async/await instead of using the callback
async function verify_unique_user(req, h) {
const user = await User.findOne({
$or: [{email: req.payload.email}, {username: req.payload.username}]
});
// Check if username exists.
if (user.username === req.payload.username) {
throw Boom.badRequest("Username taken!");
}
// Check if email exists.
if (user.email === req.payload.email) {
throw Boom.badRequest("Email taken!");
}
return req;
}
Take a look at the toolkit(h) and options.response.failAction of route.
A route can set response.failAction in options. There, you can format error messages, and send response, however you please. That includes errors thrown from pre handlers.
Edit: Every pre-handler can have it's own 'failAction' handler. You must do a response(...).takeover() if you want to halt the chain.

Promise either never get called, or is rejected (Parse JS SDK)

I am trying to write a function that add or edit some fields on a User object.
The problem come when I try to save the user, if I use user.save, the Promise is rejected with error 206 UserCannotBeAlteredWithoutSessionError.
However, if I get the session id (and documentation about that is scarce), the promise never get resolve, nor rejected. The app seems to just jump to the callback.
My function:
function update(user, callback) {
let query = new Parse.Query(Parse.User);
query.equalTo("username", user.email);
query.find().then(
(users) => {
if(users.length === 0) {
callback('Non existent user');
} else {
let user = users[0];
// user.set('some', 'thing');
console.log('save');
user.save(/*{
sessionToken: user.getSessionToken()
}*/).then(
(test) => {
console.log('OK - ' + test);
callback();
}, (err) => {
console.log('ERR- ' + require('util').inspect(err));
// console.log(callback.toString());
callback(error.message);
}
);
}
},
(error) => {
callback(error.message);
}
);
}
Called with:
var async = require('async'),
baas = require('./baas.js');
async.waterfall([
(callback) => {
callback(null, {
email: 'user#test.com',
password: 'password'
});
},
(user, callback) => {
console.log('connect');
baas.connect(() => { //Initialize the connection to Parse, and declare use of masterKey
callback(null, user);
});
},
(user, callback) => {
console.log('update');
baas.update(user, (err) => {
callback(err);
});
}
], (err) => {
console.log('Error: ' + err);
});
The logs become:
Without session token:
connect
update
save
ERR- ParseError { code: 206, message: 'cannot modify user sA20iPbC1i' }
With session token:
connect
update
save
I do not understand how it is possible that the promise just callback without printing anything, nor why no error are raised anywhere.
Edit:
Following #user866762 advice, I tried to replace the query with Parse.User.logIn and use the resulting User object.
While this solution give me a sessionToken, the end result is the same, parse crash if I don t provide the session token, or give me a error if I do.
According to the Parse Dev guide:
...you are not able to invoke any of the save or delete methods unless the Parse.User was obtained using an authenticated method, like logIn or signUp.
You might also try becoming the user before saving, but I have my doubts that will work.
When you're "get[ting] the session id" my guess is that you're really breaking something. Either Parse is having a heart attack at you asking for the session token, or when you're passing it in save you're causing something there to explode.

Categories

Resources