Use updated req variable in middleware after route execution - express

I have a middleware function I wrote to perform some logging for each HTTP request that comes in
export const winstonMiddlewareLogger = (req: express.Request, res: express.Response, next: express.NextFunction) => {
let _startTime = new Date();
res.on("finish", function() {
let responseTime = new Date().getTime() - _startTime.getTime() + "ms";
let method = req.method;
let url = req.originalUrl;
let body = req.body ? req.body : {};
let query = req.query;
let params = req.params;
let status = res.statusCode;
let msg = `HTTP ${req.method} ${req.url} ${responseTime}`;
let logDocument = {
msg,
method,
url,
params,
query,
body,
status,
responseTime,
};
logger.info(undefined, logDocument)
});
next();
};
Next thing I have a route
app.post("/slow", (req, res) => {
req.testParam = "test";
res.send("hello");
});
I want to be able to access the new property of the req parameter that is initiated in the /slow (testParam), in the res.on(...){...} event listener in my middleware function the next way:
if (req.hasOwnProperty('testParam')) {
console.log(req.testParam)
}
But the current state is that the req parameter is not updated no matter what I do in the route itself, because the req parameter it knows is only the one I get at the beginning of each request.
Changing the req variable in the route doesn't change it in the middleware which has already got a req variable of its own.
Any idea how to do it?

Ok so I found winston-express which actually does the things I wanted to.
What it does is to reassign the res.end function to a function he wrote himself.
This function can access the modified objects of req and res after the route has already executed.
So for the sake of showing how it works, I change my route to:
app.get("/", (req, res) => {
req._attributeFromRoute = "test";
res.send("hello");
});
And my middleware function looks something like this:
export const winstonMiddlewareLogger2 = (req: express.Request, res: express.Response, next: express.NextFunction) => {
let _startTime = new Date();
let end = res.end;
res.end = (chunk: any, encoding: string, cb?: () => void): void => {
res.end = end;
res.end(chunk, encoding, cb);
console.log(req._attributeFromRoute) // Notice that this property was not declared before the route execution, and now we can access it after it was defined
let responseTime = new Date().getTime() - _startTime.getTime() + "ms";
let method = req.method;
let url = req.originalUrl;
let body = req.body ? req.body : {};
let query = req.query;
let params = req.params;
let status = res.statusCode;
let msg = `HTTP ${req.method} ${req.url} ${responseTime}`;
let logDocument = {
msg,
method,
url,
params,
query,
body,
status,
responseTime,
};
logger.info(undefined, logDocument)
};
next();
};
And the way it will work is that the middleware function will wait for the route to execute and call the req.end function, which will enter the end function we created ourselfs.
We will then execute the original end function that had as a reference in the end variable.
Then we can do whatever we want with the updated objects of req and res.

Related

express deprecated res.send(status, body): Use res.status(status).send(body) instead

is anything I am doing wrong because I am new to this, I got express deprecated res.send(status, body): Use res.status(status).send(body) instead error when I carry out the update operation using PUT in postman.
router.put("/admin/update_profile/:id", async (req, res) => {
try {
const id = req.params.id;
const updatedData = req.body;
const options = { new: true };
const result = await SomeModel.findByIdAndUpdate(id, updatedData, options);
res.send(result);
// res.status(200).send(result)
} catch (err) {
res.send("Error ", err);
// res.status(404).send("Error ", err)
}
});
Pretty Much what the error says. Express has deprecated res.send(status, body);.
Replace that line with res.status(status).send(body);
Please note that writing only res.send(body); will send a status of 200 by default, so remember to use .status() in failure cases.
res.send("Error ", err);
You're passing two parameters to res.send() which as the warning message indicates is deprecated but still supported.
It's interpreting the string literal "Error " as a response status code which is not valid.
You should only pass a single parameter to send() and set the status via status()
res.status(500).send(err);
router.put("/admin/update_profile/:id", async (req, res) => {
try {
const id = req.params.id;
const updatedData = req.body;
const options = { new: true };
const result = await SomeModel.findByIdAndUpdate(id.trim(), updatedData,
options);
res.status(200).send(result);
} catch (err) {
res.status(404).send(err);
}
});

Can't send the body of my Post request to my mongoDb database, using mongoos

I'm using Node and express for my backend request.
I'm trying to create a new member, the HTTP request goes through and I can see the Id in my database but the body does not show up! here is the code:
async function createMember(membersData) {
let newMember = new Members(membersData)
console.log("this is a new member", newMember)
let createdMember = await newMember.save()
console.log("saving members info", createdMember)
return createdMember.id;
}
//route
router.post('/create', async (req, res) => {
let newMember = req.body
let createdId = await membersListModel.createMember(newMember)
res.send(createdId)
console.log("from API this is a new member", createdId)
})

Express router's mock post handler doesn't work as expected

I have a documents router which has router.post('/mine', [auth, uploadFile], async (req, res) => { ... }) route handler. The actual implementation of this route handler is below.
documents.js router
const createError = require('./../helpers/createError');
const auth = require('./../middlewares/auth');
const uploadFile = require('./../middlewares/uploadFile');
const express = require('express');
const router = express.Router();
router.post('/mine', [auth, uploadFile], async (req, res) => {
try {
let user = await User.findById(req.user._id);
let leftDiskSpace = await user.leftDiskSpace();
if(leftDiskSpace < 0) {
await accessAndRemoveFile(req.file.path);
res.status(403).send(createError('Your plan\'s disk space is exceeded.', 403));
} else {
let document = new Document({
filename: req.file.filename,
path: `/uploads/${req.user.username}/${req.file.filename}`,
size: req.file.size
});
document = await document.save();
user.documents.push(document._id);
user = await user.save();
res.send(document);
}
} catch(ex) {
res.status(500).send(createError(ex.message, 500));
}
});
module.exports = router;
I'm currently writing integration tests using Jest and Supertest. My current documents.test.js test file is below:
documents.test.js test file
const request = require('supertest');
const { Document } = require('../../../models/document');
const { User } = require('../../../models/user');
const fs = require('fs');
const path = require('path');
let server;
describe('/api/documents', () => {
beforeEach(() => { server = require('../../../bin/www'); });
afterEach(async () => {
let pathToTestFolder = path.join(process.cwd(), config.get('diskStorage.destination'), 'user');
// Remove test uploads folder for next tests
await fs.promises.access(pathToTestFolder)
.then(() => fs.promises.rm(pathToTestFolder, { recursive: true }))
.catch((err) => { return; });
// Remove all users and documents written in test database
await User.deleteMany({});
await Document.deleteMany({});
server.close();
});
describe('POST /mine', () => {
it('should call user.leftDiskSpace method once', async () => {
let user = new User({
username: 'user',
password: '1234'
});
user = await user.save();
let token = user.generateAuthToken();
let file = path.join(process.cwd(), 'tests', 'integration', 'files', 'test.json');
let documentsRouter = require('../../../routes/documents');
let errorToThrow = new Error('An error occured...');
user.leftDiskSpace = jest.fn().mockRejectedValue(errorToThrow);
let mockReq = { user: user };
let mockRes = {};
documentsRouter.post = jest.fn();
documentsRouter.post.mockImplementation((path, callback) => {
if(path === '/mine') {
console.warn('called');
callback(mockReq, mockRes);
}
});
const res = await request(server)
.post('/api/documents/mine')
.set('x-auth-token', token)
.attach('document', file);
expect(documentsRouter.post).toHaveBeenCalled();
expect(user.leftDiskSpace).toHaveBeenCalled();
});
});
});
I create mock post router handler for documents.js router. As you can see from mockImplementation for this route handler, it checks if the path is equal to '/mine' (which is my supertest endpoint), then calls console.warn('called'); and callback. When I run this test file, I can not see any yellow warning message with body 'called'. And also when POST request endpoint /api/documents/mine the server doesn't trigger my mock function documentsRouter.post. It has never been called. So I think the server's documents router is not getting replaced with my mock post route handler. It still uses original post route handler to respond my POST request. What should I do to test if my mock documentsRouter.post function have been called?
Note that my User model has a custom method for checking left disk space of user. I also tried to mock that mongoose custom method but It also doesn't work.

Async/Await result is empty after await execution

I have a simple function:
var result = '';
var url = 'http://someurl.com/?action=perform-action';
(async function() {
let a = await fetch(url)
result = await a.text();
})()
console.log(result)
I'm trying to put the result into variable but it returns nothing.
My function simply checks if an email entered into an input exists in our database. Result is "Exists" or "OK". Works fine when I send it to the console but I need the result to perform other actions. If I save it to result it shows nothing.
Eventually your result variable will include the response from the fetch, but you're displaying the result before the fetch has completed its call.
Let's annotate the order:
var result = ''; // 1
var url = 'http://someurl.com/?action=perform-action'; // 2
(async function() {
let a = await fetch(url); // 5
result = await a.text(); // 6
})() // 3
console.log(result); // 4
If you moved your console.log call into the async function after the result is set you'd achieve your goal, like so:
var result = '';
var url = 'http://someurl.com/?action=perform-action';
(async function() {
let a = await fetch(url)
result = await a.text();
console.log(result);
})()
If the goal is to do something with that result outside the async call -- like somewhere else in your platform -- you'd need to observe the changes, or call an 'after result' function (which could be an injected callback.)
Here's a working example:
var result = '';
var url = 'https://jsonplaceholder.typicode.com/todos/1';
(async function() {
const a = await fetch(url);
result = await a.text();
console.log(result);
})()

Nodejs: returning result on async result

I'm trying to code an RESTfull API in nodejs which is basically around a controller/modele schema and I meet some problems about the async nature of nodejs:
Station.js: (controller)
'use strict';
var url = require('url');
var Stations = require('./StationsService');
module.exports.stationsGet = function stationsGet(req, res, next){
var result = Stations.stationsGet(req.swagger.params['arg']);
if(typeof result !== 'undefined') {
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify(result || {}, null, 2));
}
else
res.end();
};
StationService.js: (modele)
'use strict';
exports.stationsGet = function(param){
var data_output = {};
var sql = 'SELECT * FROM foo WHERE args = ${foo}';
db.execute(sql, {foo: param}, db.queryResult.any, function(result){
// 'result' containing the query data
});
// Ideally: data_output = result;
return data_output;
}
The problem is if I use callback on my db.execute to continue, I have to give all the controller context (res, ...) to reply back to the client, and it break the modele/controller schema since my modele does the remaining controller work.
Is there a (easy?) way to get the result of the query in stationsGet() and then returning it?
Is it really against the nodejs nature and if so how to adopt the correct behavior in that case?
PS: I'm using swagger which has generated the files and base structure for nodejs.
You should use a callback in this case (take a look at promises as well)
your controller will look like this:
'use strict';
var url = require('url');
var Stations = require('./StationsService');
module.exports.stationsGet = function stationsGet(req, res, next){
Stations.stationsGet(req.swagger.params['arg'], function(err, result) {
if(typeof result !== 'undefined') {
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify(result || {}, null, 2));
}
else
res.end();
});
};
And the model you made must accept a callback as a last parameter, and you return err and the result like follows:
'use strict';
exports.stationsGet = function(param, cb){
var data_output = {};
var sql = 'SELECT * FROM foo WHERE args = ${foo}';
db.execute(sql, {foo: param}, db.queryResult.any, function(result){
cb(null, result); // first parameter is the error and the second is the result, this is pretty standard in node
});
}
I hope this helps you