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,
};
Related
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();
}
};
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)
}
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);
}
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)
I have defined multiple route middleware and want to share them across multiple routes/controllers.
Here is my setup:
app.js requires ./routes/index.js:
// load fs module
var fs = require('fs');
// import routing files
module.exports = function(app){
fs.readdirSync(__dirname).forEach(function(file) {
if (file == "index.js") return;
var name = file.substr(0, file.indexOf('.'));
require('./' + name)(app);
});
};
index.js loads all routes automaticly in the dir. A possible routes file can look like:
module.exports = function(app) {
app.get('/contacts', function(req, res, next) {
// routing stuff
});
};
Now I got route middleware:
function isAuthenticated(req, res, next) {
if (!req.session.authenticated) return next(new Error('user not authenticated'));
};
function loadUser(req, res, next) {
var query = User.findById(req.session.user_id);
query.populate('contacts');
query.exec(function(err, user) {
if (err) return next(err);
req.user = user;
next();
});
}
which I want to use like:
var User = require('../models/user');
module.exports = function(app) {
app.get('/contacts', isAuthenticated, loadUser, function(req, res, next) {
res.json(req.user.contacts);
});
};
I also would like to avoid requiring them accross all routing files.
A possible solution would also be:
// load fs module
var fs = require('fs');
var routeMiddleware = {
loadUser: function(req, res, next) { // logic },
isAuthenticated: function(req, res, next) { // logic },
};
// import routing files
module.exports = function(app){
fs.readdirSync(__dirname).forEach(function(file) {
if (file == "index.js") return;
var name = file.substr(0, file.indexOf('.'));
require('./' + name)(app, routeMiddleware);
});
};
but I think not the best...
Personally I would declare shared middleware in the app, not in the controllers, i.e.:
routes/home.js:
module.exports = function(req, res, next) { \\ Code }
app.js:
app.get('/', thisMiddleware, thatMiddleware, require('./routes/home'))
You can also make a stack (array, not an object):
theseMiddlewares = [thisMiddleware, thatMiddleware]
app.get('/', theseMiddlewares, require('./routes/home'))
And if these middlewares are used on all routes except a few, you can do the following:
theseMiddlewares = function(req, res, next) {
if (req.url.match(some_regex_for_urls_to_skip)) next()
else {
\\ do stuff...
next()
}
}
Now you can app.use(theseMiddlewares) that middleware, or if it needs to happen in a certain order relative to other middleware, you can use app.all('*', theseMiddlewares)