how get the req, res objects in the callbacks - javascript

I like to get the req, res, next objects outside the middleware function.
sample middleware - sample.js:
var app = express();
....
.....
....
var updateUserInput = {
init:function(){
get_data_from_db(params, function(){
// want req, res here.
})
}
}
const processUserInput = function(req, res, next){
updateUserInput.init();
}
module.exports = processUserInput;

You should be able to pass those into the init function right away:
var updateUserInput = {
init:function(req,res,next){
get_data_from_db(params, function(){
// You will have req and res available here
// finish processing and call next()
})
}
}
const processUserInput = function(req, res, next){
updateUserInput.init(req,res,next);
}

Related

Node JS async/await controller issue

I am trying to use an asynchronous function in my Node API controller, but am receiving an error from my 'error-handler' middleware.
TypeError: fn is not a function
at eval (webpack:///./app/middleware/errorHandler.js?:16:21)
It does not like my 'findAll' function exported from my controller, why is this not a function? Am I exporting the function correctly? Am I using async/await correctly? Do I need a polyfill for this? I understood that async/await was supported from Node v8. I am currently running Node v11.10 and Express v4.16.4.
Here is my routes file:
// routes.js
const verifyToken = require('../../middleware/verifyToken.js');
const errorHandler = require('../../middleware/errorHandler.js');
module.exports = app => {
const controller = require('../../controllers/controller.js');
app.get(`/collection`, verifyToken, errorHandler(controller.findAll));
}
Here is my controller:
// controller.js
exports.findAll = async (req, res) => {
const collections = await collection.find().populate('other');
res.send(collections);
};
Here is my middleware:
// errorHandler.js
module.exports = fn => {
return (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
};
Any help is greatly appreciated.
Im not sure but is errorHandler expecting fn to be the error? If so, why is it called passing (req, res next)?
I use following structure:
Router
// routes.js
const verifyToken = require('../../middleware/verifyToken.js');
const controller = require('../../controllers/controller.js');
var router = express.Router()
router.route('/collection').get(
verifyToken,
controller.findAll
)
module.exports = router
Controller
// controller.js
const asyncUtil = fn =>
function asyncUtilWrap(req, res, next, ...args) {
const fnReturn = fn(req, res, next, ...args)
return Promise.resolve(fnReturn).catch(next)
}
module.exports = {
findAll: asyncUtil(async (req, res, next) => {
const collections = await collection.find().populate('other'); // you can do try/catch here if you want to handle the error here
res.send(collections);
};
Then Error Handler usually goes at bottom of app.js (but you can place it at bottom of your router):
// app.js
app.use(function(err, req, res, next) {
res.status(err.status || 500)
res.send(err.message)
})
I believe this is how I would do it if I understand you correctly:
// routes.js
const verifyToken = require('../../middleware/verifyToken.js');
const controller = require('../../controllers/controller.js');
module.exports = app => {
app.get(`/collection`, verifyToken, controller.findAll);
}
// controller.js
exports.findAll = async (req, res, next) => {
try {
const collections = await collection.find().populate('other');
res.send(collections);
} catch(err) {
console.log(err); // up to you what to do with the error
next();
}
};

Can a POST request accept two callbacks and can the first pass data to the second?

I have the following .post() request:
const express = require('express');
const router = express.Router();
const search_controller = require('../controllers/searchController');
const result_controller = require('../controllers/resultController');
//Search Routes
router.post('/', search_controller.search_create_post);
module.exports = router;
Could I add a second callback to it so that the first callback is run, then the second callback as such:
router.post('/', search_controller.search_create_post, result_controller.result_create_post)
Would I need a next() somewhere in those create functions? And could I also pass data from the search_create_post callback to the result_create_post callback? I would want to pass in the newly created Search object's id.
My current search_controller.search_create_post function is a such:
exports.search_create_post = (req, res, next) => {
let newSearch = new Search({ search_text: req.body.search_text });
newSearch.save((err, savedSearch) => {
if (err) {
console.log(err);
} else {
res.send(savedSearch);
}
})
};
You might be able to use like this (based on how your functions are written):
// option A
router.post('/', search_controller.search_create_post, result_controller.result_create_post)
// options B
router.post('/', search_controller.search_create_post)
router.post('/', result_controller.result_create_post)
If search needs to pass data to result, you could set req.search_data in search_create_post and then get the value in result_create_post.
Take a look at https://expressjs.com/en/guide/using-middleware.html. There are a few good examples on this page.
app.get('/user/:id', function (req, res, next) {
console.log('ID:', req.params.id)
next()
}, function (req, res, next) {
res.send('User Info')
})
// handler for the /user/:id path, which prints the user ID
app.get('/user/:id', function (req, res, next) {
res.end(req.params.id)
})
based on your comment below:
You might be able to do this...
exports.search_create_post = (req, res, next) => {
let newSearch = new Search({ search_text: req.body.search_text });
newSearch.save((err, savedSearch) => {
if (err) {
console.log(err);
} else {
req.searchData = savedSearch;
}
next();
})
};

Express.js - wrap every middleware/route in "decorator"

I have Express.js instance and couple of routes which I want to wrap in some function. Example:
const wrapper = (route) => {
return (req, res, next) => {
let result = route(req, res, next);
// do some independent processing
}
};
app.get('/', wrapper((req, res, next) => {
// respond to request somehow
}));
While this works fine, I don't like the idea to explicitly call wrapper on every route or middleware which requires such processing.
Is there any way to be able to wrap every required route/middleware in certain wrapper (given that wrapper function can check that this route/middleware needs to be wrapped) implicitly (via Express.js extension, monkey-patching or some special middleware)?
UPDATE:
More solid example. Let's assume I want to make an async router functions. But I don't want to catch errors in each and every route function. So I wrap them up:
const wrapper = func => (req, res, next) => {
const promise = func(req, res, next);
if (promise.catch) {
promise.catch(err => next(err));
}
next();
};
app.get('/one', wrapper(async (req, res, next) => {
// respond to request somehow
}));
app.get('/two', wrapper(async (req, res, next) => {
// respond to request somehow
}));
app.get('/three', wrapper(async (req, res, next) => {
// respond to request somehow
}));
// and so on...
app.use((err, req, res, next) => {
// do something with intercepted error
});
This explicit wrapper around all routes is actually the thing I want to get rid of.
It turned out to be a bit of a PITA because, ultimately, Express doesn't propagate the return value of a route handler function.
This is what I came up with (a monkey-patch):
const Layer = require('express/lib/router/layer');
const handle_request = Layer.prototype.handle_request;
Layer.prototype.handle_request = function(req, res, next) {
if (! this.isWrapped && this.method) {
let handle = this.handle;
this.handle = function(req, res, next) { // this is basically your wrapper
let result = handle.apply(this, arguments);
// do some independent processing
return result;
};
this.isWrapped = true;
}
return handle_request.apply(this, arguments);
};
I would probably suggest using a similar approach as express-promise-router though, which implements a drop-in replacement for Express' Router. However, it's not implicit.
Why not just use next()?
You can add stuff on req like
app.get('/', (req, res, next) => {
req.somestupidfieldthatidontevenknowwhyinamedthisway = 42;
next();
});
app.get('/', (req, res, next) => {
//req.somestupidfieldthatidontevenknowwhyinamedthisway is now accessible as 42
var valueFromPreviousMiddleware = req.somestupidfieldthatidontevenknowwhyinamedthisway;
.....
});
You could wrap middleware and router as below
function wrapper(func) {
return function inner(req, res, next) {
const start = Date.now();
func(req, res, function () {
let elapsedMS = Date.now() - start
console.log('time elapsed for function ' + func.prototype.constructor.name + ' is ' + elapsedMS)
next.apply(this, arguments);
});
};
}
var originalAppUse = app.use;
app.use = function () {
lastArg = arguments.length - 1;
if (typeof arguments[lastArg] === 'function') {
arguments[lastArg] = wrapper(arguments[lastArg])
}
originalAppUse.apply(this, arguments)
}

Providing Custom Params In Express Middleware

I am having a problem with my Node.js app. In short I want to pass custom parameters into my middleware function other than just req, res, and next.
Middleware file:
var DB = require('./DB.js');
function requirePermissions(e) {
console.log('nope')
}
module.exports = requirePermissions;
Route:
router.post('/posts', requirePermissions('post_creation'), function(req, res) {
var o = req.body,
title = o.post.title,
content = o.post.content;
res.send('made it');
});
I have confirmed that using function requirePermissions(req, res, next) {} will work, but I do not understand how to include my own parameters.
Your function requirePermissions should return another function which will be the actual middleware:
function requirePermissions(e) {
if (e === 'post_creation') {
return function(req, res, next) {
// the actual middleware
}
} else if (e === 'something_else') {
return function(req, res, next) {
// do something else
}
}
}
You can also do it like that:
function requirePermissions(e) {
return function(req, res, next) {
if ('session' in req) {
if (e === 'post_creation') {
// do something
} else if (e === 'something_else') {
// do something else
}
}
}
}
You can just create an anonymous function for your middleware that lets you call your actual function with some additional arguments:
router.post('/posts', function(req, res, next) {
requirePermissions('post_creation', req, res, next);
}, function(req, res) {
var o = req.body,
title = o.post.title,
content = o.post.content;
res.send('made it');
});
Or, you can use .bind() to preprend arguments:
router.post('/posts', requirePermissions.bind('post_creation'), function(req, res) {
var o = req.body,
title = o.post.title,
content = o.post.content;
res.send('made it');
});
This will call your requirePermissions() functions with four arguments like this:
requirePermissions('post_creation', req, res, next)

Export these REST API functions in node.js

I am trying to export some REST API functions out of a module. I am using node.js restify.
I have a file called rest.js which contains the API.
module.exports = {
api_get: api_get,
api_post: api_post,
};
var api_get= function (app) {
function respond(req, res, next) {
res.redirect('http://127.0.0.1/login.html', next);
return next();
}; //function respond(req, res, next) {
// Routes
app.get('/login', respond);
}
var api_post= function (app) {
function post_handler(req, res, next) {
};
app.post('/login_post', post_handler);
}
The APIs are called in this manner;
var rest = require('./rest');
var server = restify.createServer({
name: 'myapp',
version: '1.0.0'
});
rest.api_get(server);
rest.api_post(server);
The error encountered is TypeError: rest.api_get is not a function
Your mistake was to export the function variables before they were defined. The correct way is do the export at the bottom. It is also good practice to do it this way all the time. THe correct code would look like this;
var api_get= function (app) {
function respond(req, res, next) {
res.redirect('http://127.0.0.1/login.html', next);
return next();
}; //function respond(req, res, next) {
// Routes
app.get('/login', respond);
}
var api_post= function (app) {
function post_handler(req, res, next) {
};
app.post('/login_post', post_handler);
}
module.exports = {
api_get: api_get,
api_post: api_post,
};

Categories

Resources