Unable to modified/appened REQUEST in middleware - javascript

I am new to nodejs and typescript and I want to add a new parameter in req.body say req.body.jwt_token.
I am using a middleware to update the request data model. The issue is that i am able to access (console.log works) the new key req.body.jwt_token just work in that function and is not accessible(don't even exist) apart from that.
I want to use req.body.jwt_token in some controller.
export function httpsProtocol(req: Request, res: Response, next: NextFunction) {
try {
if (req.headers.authorization != undefined) {
let authorization = req.headers.authorization;
let authorizationArr: string[] = authorization.split('Bearer')
if (authorizationArr[1] != undefined) {
let jwtToken = "Bearer " + authorizationArr[1].trim();
req.headers.Authorization = jwtToken;
req.body.jwt_token = authorizationArr[1].trim();
console.log(req.body.jwt_token); //able to console this
}
}
} catch (error) {
return res.status(422).json({
message: "something goes wrong",
error: error
});
}
next();
};
Please suggest the solution for this problem. how can i achieve this in nodejs and typescript. I am using express as framework
Thank You

💡 The only reason why you can not access req.body.jwt_token in your controller is that before you set the value, you next().
👨🏽‍🏫 Make sure to add your next() inside if/else condition. So, you can copy this code below 👇 and use it:
export function httpsProtocol(req: Request, res: Response, next: NextFunction) {
try {
if (req.headers.authorization != undefined) {
let authorization = req.headers.authorization;
let authorizationArr: string[] = authorization.split('Bearer')
if (authorizationArr[1] != undefined) {
let jwtToken = "Bearer " + authorizationArr[1].trim();
req.headers.Authorization = jwtToken;
req.body.jwt_token = authorizationArr[1].trim();
console.log(req.body.jwt_token); //able to console this
// your next here
next();
} else {
next(); // next or do some stuff
}
} else {
next(); // next or do some stuff
}
} catch (error) {
return res.status(422).json({
message: "something goes wrong",
error: error
});
}
// next(); your next here only make your req.body.jwt_token is undefined
};
Maybe this answer will help you to know the reason: passing value from middleware to controller in restify using req.data is not working?
I hope it can help you 🙏.

Related

How To Deal With Errors In Express Controller / Service Setup?

I have a challenge where I can't really make a decision on how to deal with HTTP responses / Errors in my services and controllers in my Express API. My goal is to have the services be responsible for one thing only and not deal with HTTP at all. Atleast that's my thought.
I would love some feedback on my approach...
I have added general error middlewares:
const errorResponder = (error, req, res, next) => {
if (error.statusCode && error.message) {
return res.status(error.statusCode).send(error.message);
}
if (error.statusCode) {
return res.status(error.statusCode).send();
}
if (error.message) {
return res.status(500).send(error.message);
}
return next(error); // Forward if above is't triggered
};
const errorFailSafe = (error, req, res, next) => {
console.log("Fail safe");
res.status(500).send("Something went wrong, we are digging into it!");
};
And then in my controller I unwrap what I need from the req and send to a service. Afterward I send the response back to the client.
findUser: async (req,res,next) => {
const userId = req.params.userId;
try {
// Call service
const user = await UserService.findOne(userId);
// Send user back to client
res.status(200).send(user);
} catch (error) {
return next(error)
}
}
In my service using Sequelize:
findOne: async (userId) => {
try {
let user = await db.users.findByPk(userId);
if (user == null) {
throw new NotFound("User not found");
}
return user;
} catch (error) {
throw error;
}
};
The NotFound error is a custom error class extending Error.
class NotFound extends Error {
constructor(message) {
super(message);
this.statusCode = 404;
}
}
module.exports = NotFound ;
Here I kinda break the seperation by having the Service deal with HTTP by calling NotFound.
I could change this so it's the Controller doing the check. Would that be better?
Any feedback would be appreciated. :)

Callback to Async/await

I have been trying to convert my existing Node JS code from function callback to Async Await, because a new SDK came out and callbacks are deprecated. These are the related codes. The code is communicating and publishing to a mqtt broker.
First here I call the ToggleX method
super.controlToggleX(channel, value, (err, res) => {
if (err) {
this.log(`Toggle Response: err: ${err}`);
}
this.log(`Toggle Response: res: ${JSON.stringify(res)}`);
});
After that in the controlToggleX method I set the payload for the message and make the publlish to the broker.
controlToggleX(channel, onoff, callback) {
const payload = { togglex: { channel, onoff: onoff ? 1 : 0 } };
return this.publishMessage('SET', 'Appliance.Control.ToggleX', payload, callback);
}
In the publishMessage I compile the message to the broker and publish it. After publishing I waiting for the response and to keep track of the ongoing messages I create the waitingMessageIds array.
publishMessage(method, namespace, payload, callback) {
this.clientResponseTopic = `/app/${this.userId}-${appId}/subscribe`;
const messageId = crypto.createHash('md5').update(generateRandomString(16)).digest('hex');
const timestamp = Math.round(new Date().getTime() / 1000); // int(round(time.time()))
const signature = crypto.createHash('md5').update(messageId + this.key + timestamp).digest('hex');
const data = {
header: {
from: this.clientResponseTopic,
messageId,
method,
namespace,
payloadVersion: 1,
sign: signature,
timestamp,
},
payload,
};
this.client.publish(`/appliance/${this.uuid}/subscribe`, JSON.stringify(data));
if (callback) {
this.waitingMessageIds[messageId] = {};
this.waitingMessageIds[messageId].callback = callback;
this.waitingMessageIds[messageId].timeout = setTimeout(() => {
// this.log('TIMEOUT');
if (this.waitingMessageIds[messageId].callback) {
this.waitingMessageIds[messageId].callback(new Error('Timeout'));
}
delete this.waitingMessageIds[messageId];
}, 20000);
}
this.emit('rawSendData', data);
return messageId;
}
When a new message comes from the broker I check the waitingMessageIds array, the messageId is in the array? If yes I delete the Timer and process the message with the callback coming from the publishing.
this.client.on('message', (topic, message) => {
if (!message) return;
// message is Buffer
try {
message = JSON.parse(message.toString());
} catch (err) {
this.emit('error', `JSON parse error: ${err}`);
return;
}
if (message.header.from && !message.header.from.includes(this.uuid)) return;
if (this.waitingMessageIds[message.header.messageId]) {
if (this.waitingMessageIds[message.header.messageId].timeout) {
clearTimeout(this.waitingMessageIds[message.header.messageId].timeout);
}
this.waitingMessageIds[message.header.messageId].callback(null, message.payload || message);
delete this.waitingMessageIds[message.header.messageId];
} else if (message.header.method === 'PUSH') {
const namespace = message.header ? message.header.namespace : '';
this.log('Found message');
this.emit('data', namespace, message.payload || message);
}
this.emit('rawData', message);
});
mqtt package is working with callback, but the async-mqtt is returning Promise so it is going to be good for me.
I was successfull to publish with it, and after that point I put the messageId to the array and start a timer, but when the reply came i was not been able to proocess the waitingMessageIds and return to the original point (super.controlToggleX).
Could somebody please help me. Thank
Edit:
I tried to rewrite PublishMessage with async-mqtts and it looks like this:
async publishMessage(method, namespace, payload) {
.
.
.
try {
await this.client.publish(`/appliance/${this.uuid}/subscribe`, JSON.stringify(data));
} catch (err) {
return new Error(err);
}
this.waitingMessageIds[messageId] = {};
// this.waitingMessageIds[messageId].callback = callback;
this.waitingMessageIds[messageId].timeout = setTimeout(() => {
// this.log('TIMEOUT');
if (this.waitingMessageIds[messageId].callback) {
this.waitingMessageIds[messageId].callback(new Error('Timeout'));
}
delete this.waitingMessageIds[messageId];
}, 20000);
this.emit('rawSendData', data);
return messageId;
}
Because with the await publish waits for the response I do not need check if it is a callback, i just put the messageId into the waitingmessageIds array.
When I process the incoming message in this.client.on('message' I don not know how to change this -> this.waitingMessageIds[message.header.messageId].callback(null, message.payload || message);

How do I send more than one error at a time when validating?

When I create a POST request for I need to validate the following fields: first_name, last_name, mobile_number, reservation_date, reservation_time and people(party size).
Right now I have a middleware function that checks if any of the fields are missing:
function hasProperties(...properties) {
return function (res, req, next) {
const { data = {} } = res.body;
try {
properties.forEach((property) => {
if (!data[property]) {
const error = new Error(`${property}`);
error.status = 400;
throw error;
}
});
next();
} catch (error) {
next(error);
}
};
}
Then in my controller:
const hasAllProps = hasProperties(
'first_name',
'last_name',
'mobile_number',
'reservation_date',
'reservation_time',
'people'
);
This is working great however I have to add additional validation to several of the fields. I have 2 additional functions: one is making sure the people field is a number, and the other is making sure the reservation_date is a date:
const validPeople = (req, res, next) => {
const { people } = req.body;
if (Number.isInteger(people)) {
return next();
}
next({ status: 400, message: 'people' });
};
const validDate = (req, res, next) => {
const { reservation_date } = req.body;
if (reservation_date instanceof Date) {
return next();
}
next({ status: 400, message: 'reservation_date' });
};
Then I pass them all in to my exports:
create: [hasAllProps, validDate, validPeople]
I am only ever able to send one error at a time, in this case its validDate because it comes before validPeople in the exports array. I am unable to throw all of my errors into an array because I need to response with:
status: 400, message: '<the-specific-field>'
Is there a way to individually send all these error messages?
As the other response has stated, if you're trying to send multiple responses, that's not possible. You can, however, construct an array of the errors.
You could technically pass data between middleware... (Can I send data via express next() function?)
... but my recommendation would be to be to try to merge them into a single middleware. For example, hasAllProps, validPeople, and validDate should ideally all take in a req and return null or an error. Then you could do:
function validDate(req) {
return null;
}
function validOtherProp(req) {
return 'error_here';
}
function anotherValidation(req) {
return 'second_error';
}
const errorCollectorMiddleware = (...validators) =>
(req, res, next) => {
const errors = validators.map(v => v(req)).filter(error => error !== null);
if (errors.length > 0) {
next({
status: 400,
errors
})
} else {
next();
}
}
// This is how you construct a middleware
const middleware = errorCollectorMiddleware(validDate, validOtherProp, anotherValidation);
// And here's a test. You wouldn't do this in your actual code.
console.log(middleware(null, null, console.log))
/*
{
"status": 400,
"errors": [
"error_here",
"second_error"
]
}
*/
With HTTP/S you cannot have one request two responses. The client system sends the request, receives the response and does not expect a second response.

lowdb updates only one infos in JSON file

I make a lowdb request to update a JSON file, but only the date is updated. When I make a console.log of the information to update, this is what I want to update:
res.on("end", function () {
let body = Buffer.concat(chunks);
let final = JSON.parse(body);
if (final.error !== undefined) {
console.log("Initial authentication:", final.error_description, "Please refresh the authentication grant");
extAuthCallback(84);
} else {
tokens.set('access_token', final.access_token)
.set('expires_in', final.expires_in)
.set('refresh_token', final.refresh_token)
.set('refresh_date', moment())
.write()
console.log(final, tokens.value())
extAuthCallback(1);
}
});
console.log of my final variable:
{
access_token: 'oa_prod_iq00cRPk5Jhh4VffSHlDj7DEDsSIlpCRRczI3l3ASC0',
token_type: 'bearer',
expires_in: 2399,
refresh_token: 'oa_prod_nIjBZs74xGvJXi1B-wdMyITfxGyklpCRRczI3l3ASC0'
}
console.log of my JSON file after the request:
{
access_token: 'oa_prod_pB9Q0FFM9Tk4c5n3HMRBFKAVz6naiJ-jmb3QCeBrT00',
expires_in: 2399,
refresh_token: 'oa_prod_nX3EDs530SM8eHv_fM5BN7-5RLBwkrKoUi6uExBbTY4',
refresh_date: '2020-11-28T23:31:13.855Z',
primary_autorization_date: '2020-11-29T00:40:58.421Z'
}
My JSON file after the modifications:
{
"access_token": "oa_prod_pB9Q0FFM9Tk4c5n3HMRBFKAVz6naiJ-jmb3QCeBrT00",
"expires_in": 2399,
"refresh_token": "oa_prod_nX3EDs530SM8eHv_fM5BN7-5RLBwkrKoUi6uExBbTY4",
"refresh_date": "2020-11-28T23:31:13.855Z",
"primary_autorization_date": "2020-11-29T00:40:58.421Z"
}
So it only has the primary_autorization_date field changing...
You should use set instead of update.
tokens.set('access_token', final.access_token)
.set('expires_in', final.expires_in)
.set('refresh_token', final.refresh_token)
.set('refresh_date', moment())
.write()
The update method is accepted a function like this.
db.update('test1', (n) => 5555)
.update('test2', (n) => n + 1)
.write()
If you use set, you just need to assign the value to it.
db.set('test1', 5555).set('test2', 3333).write()
And when you use moment, there are two ways to you could use.
// Way 1 with moment()
db.set('date', moment()).write()
// Way 2 with moment
db.update('date', moment).write()
So the solution is:
I call my function that contains the HTTP request in another file like this:
app.get('/', async function(req, res) {
const access_token = req.query.code;
if (access_token) {
let authentication = await asyncExtAuth(access_token);
if (authentication == 84)
return res.send({error: "Please give a valid access_token"});
res.sendFile(path.join(__dirname + '/success_page/index.html'));
db.set('access_token', access_token).write()
tokens.set('primary_autorization_date', moment()).write()
console.log("Access Token successfully refreshed")
}
else
res.send({error: "Please specify an access token"})
})
In which I modify a second time my file with the line tokens.set('primary_autorization_date', moment()).write(). By doing this, lowdb doesn't take into account the modification made just before and re-modifies my file with the information it contained before. The solution is to add the line tokens.read() just before modifying the file to update the cache:
app.get('/', async function(req, res) {
const access_token = req.query.code;
if (access_token) {
let authentication = await asyncExtAuth(access_token);
if (authentication == 84)
return res.send({error: "Please give a valid access_token"});
res.sendFile(path.join(__dirname + '/success_page/index.html'));
db.set('access_token', access_token).write()
tokens.read()
tokens.set('primary_autorization_date', moment()).write()
console.log("Access Token successfully refreshed")
}
else
res.send({error: "Please specify an access token"})
})

TypeError: res.status is not a function

I'm making a function that permits me to upload a picture to imgur in my express api (nodejs),
i'm encoutering an error when calling a function returning a promise:
TypeError: res.status is not a function
at uploadpicture.then
This is my code:
Where error is raised:
router.post('/upload', (req, res, next)=> {
var busboy = new Busboy({headers: req.headers});
busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {
if(fieldname == 'image') {
// the buffer
file.fileRead = [];
file.on('data', function(data) {
// add to the buffer as data comes in
this.fileRead.push(data);
});
file.on('end', function() {
// create a new stream with our buffered data
var finalBuffer = Buffer.concat(this.fileRead);
upload = uploadpicture(finalBuffer).then((res)=>{ //success request
console.log(res);
res.status(200).json({success: true, message: "Successfully uploaded !", url: res.data.link});
},(err)=>{ //error
res.status(500).json({success: false, message: "Error happenned while uploading !"});
}).catch((error)=>{
console.log(error);
res.status(500).json({success: false, message: "Error happenned while uploading !"});
});
})
}
});
busboy.on('finish', function() {
//busboy finished
});
req.pipe(busboy);
});
And the function :
function uploadpicture(stream){ //get picture stream
return new Promise((resolve, reject)=>{
var options = {
uri: 'https://api.imgur.com/3/image',
method: 'POST',
headers: {
//'Authorization': 'Client-ID ' + config.client_id_imgur // put client id here
},
formData: {
image: stream,
type: 'file'
},
auth: {
bearer: config.access_token_imgur,
}
};
request(options)
.then((parsedBody)=> {
resolve(parsedBody);
})
.catch((err)=> {
console.log(err);
reject(err.toString())
});
});
}
The code works perfectly, but i don't know why suddendly this error happened,
i tried to :
change arrow functions to function(){}
Add next to the route parameters
Nothing worked, Thanks for your help
The accepted answer directly addresses the OP's problem, but I post another solution since you can also encounter this error in other places.
When you have:
api.use((error: ErrorRequestHandler, request: ExpressRequest, response: ExpressResponse) => {
response.status(500).end() // response.status is not a function
})
Because the error handling route must accept 4 arguments for express to identify it as an error middleware.
api.use((error: ErrorRequestHandler, request: ExpressRequest, response: ExpressResponse, next: NextFunction) => {
response.status(500).end()
})
Just adding the next function (or whatever argument you're missing) will fix it.
https://github.com/visionmedia/supertest/issues/416#issuecomment-514508137
At this point:
upload = uploadpicture(finalBuffer).then((res)=>{ //success request
the resis the result of promise uploadpicture function (that is the parsedBody), not the res from the express route. So indeed, it has no status function. Try change the then callback name like:
upload = uploadpicture(finalBuffer).then((otherName)=>{ //success request
You are getting this error:
TypeError: res.status is not a function
Because the order should be (err, res, req, next) not (req, res, err, next),
example below
const errorHandler = (err, req, res, next) => {
const statusCode = res.statusCode === 200 ? 500 : res.statusCode;
res.status(statusCode)
res.json({
message : err.message,
stack :process.env.NODE_ENV === 'production' ? null : err.stack,
})
}
Order of parameters really matters i had error in below code
const getImagesByBrand = async (res) => {
try {
const images = await Image.find();
res.status(200).json(images);
} catch (error) {
res.status(500).json(error);
}
};
I was not giving req as parameter and that was the reason for error i just add req,res and it worked
If you are using the async/await method:
const notifications = await notifications.aggregate({...})
if(notifications){
return res.status(200).json({ data: notifications })
}else{
return res.status(404).json({ message: 'No notifications found'})
}
Make sure that you are including your return statements. Not including a return statement will cause this. Something else that I was doing is I had JSON instead of json, which will most definitely throw an error.

Categories