I've come across two different methods of defining routes in Node.js:
Method #1:
router.get("/", (req, res, next) => {
res.render("index", { title: "ABC" });
});
module.exports = router;
Method #2:
module.exports = (() => {
router.get("/", (req, res, next) => {
res.render("index", { title: "ABC" });
});
return router;
})();
I was curious, what's the main difference between these two? And, is there a major reason why one method is preferred over the other? Thanks!
Method #2 is ES6 syntax with arrow function syntax being used. Both perform the same functionality, only difference is the syntax.
Related
What is the difference between these two statements:
app.get('/',someFunction);
app.route('/').get(someFunction);
Please note I'm not comparing router.get and app.get
Suppose you want to do three routes on the same path:
app.get('/calendarEvent', (req, res) => { ... });
app.post('/calendarEvent', (req, res) => { ... });
app.put('/calendarEvent', (req, res) => { ... });
Doing it that way requires you to duplicate the route path each time.
You could instead do this:
app.route('/calendarEvent')
.get((req, res) => { ... })
.post((req, res) => { ... })
.put((req, res) => { ... });
It's basically just a bit of a shortcut if you have routes for multiple different verbs all on the same path. I've never had an occasion to use this, but apparently someone thought it would be handy.
It might be even more useful if you had some sort of common middleware that was unique to just these three routes:
app.route('/calendarEvent')
.all((req, res, next) => { ... next(); })
.get((req, res) => { ... })
.post((req, res) => { ... })
.put((req, res) => { ... });
One could also use a new router object for a similar purpose.
And, I guess I would be remiss if I didn't explain that there's no difference between just these two statements (which is part of what you asked):
app.get('/',someFunction);
app.route('/').get(someFunction);
They do exactly the same thing. The rest of my answer is about what else you can do with the second option.
I have an Express application and I'm trying to put all my middleware in its own file. Some of the middleware functions need the db object and some don't.
It's pretty straightforward for the functions that don't need the db object, but given my code structure below, how can I reference the db object in doesNotNeedDbParam since it already has params req, res, and next?
somefile.js:
const router = express.Router()
const doesNotNeedDbParam = require('./middleware')().doesNotNeedDbParam
function foo () {
// Currently I have to call require and pass in the db object here b/c
// it's not set when requiring the function doesNotNeedDbParam
router.use(require('./middleware')(db).needsDbParam // <-- Is there a better way to do this so that I can require the file above and pass the db object in when it's set?
}
// Setup db object here
foo()
middleware.js
function doesNotNeedDbParam (req, res, next) {
...
}
function needsDbParam (req, res, next) {
// Where do I reference the db variable?
}
module.exports = (db) => {
return {
doesNotNeedDbParam: doesNotNeedDbParam,
needsDbParam: needsDbParam
}
}
Functional Approach
I think a good structure for this is to try currying your middleware. This is a pattern practiced by middleware such as body-parser and internally by Express itself with serve-static. This way, you only have to require once, and pass db where you need to, and don't where you don't need it:
// Instead of declaring each function directly as a middleware function,
// we declare them as a function that returns a middleware function
function doesNotNeedDbParam () {
return function (req, res, next) {
…
}
}
function needsDbParam (db) {
return function (req, res, next) {
// You can use db here along with req, res, next
}
}
// No need to export a function now
module.exports = {
doesNotNeedDbParam,
needDbParam,
};
Then, just require:
const middleware = require('./middleware');
…
router.use(middleware.doesNotNeedDbParam()); // Since this doesn't need anything, no argument
router.use(middleware.needsDbParam(db)); // You can pass db here now
If you're comfortable with ES6+ syntax, you can condense to:
const doesNotNeedDbParam = () => (req, res, next) => {
…
}
const needsDbParam = (db) => (req, res, next) => {
// Use db here
}
// Export object here...
Then:
const { doesNotNeedDbParam, needsDbParam } = require('./middleware');
…
router.use(doesNotNeedDbParam());
router.use(needsDbParam(db));
Attach Approach
There's also another way you can do this, by attaching a property to the req object once. This removes the need to repass db every single time you want it. Many other packages use this strategy. It goes something like this:
function attachDb (db) { // Still use curry approach here since we want db
return function (req, res, next) {
// Attaches the specified db to req directly
req.db = db;
}
}
function needsDbParam (req, res, next) { // No need for currying
// Now use req.db here
}
// Export your other middleware…
Then, use it like so, make sure attachDb is first so that the property is assigned before you use it:
router.use(attachDb(db)); // Before all other middleware that depend on req.db
…
// No need to call because this is already the middleware function,
// able to use req.db, which was assigned via attachDb
router.use(needDbParam);
Why not just declare module.exports as a single function:
module.exports = (db) => {
let module = {};
module.doesNotNeedDbParam = (req, res) => {
// Do Stuff
};
module.needsDbParam = (req, res) => {
// db now in scope
};
return module;
};
This is what your somefile.js would become:
const router = express.Router();
const db = initializeDb();
const doesNotNeedDbParam = require('./middleware')().doesNotNeedDbParam;
router.use(require('./middleware')(db).needsDbParam);
You could also set it up once like this:
const middleware = require('./middleware')(db);
const doesNotNeedParam = middleware.doesNotNeedParam;
router.use(middleware.needsDbParam);
This isn't really any different than what you were doing before, but now you have access to db inside of needsDbParam. If your initializeDb function is async, then you will need to use Promise or some other async library to include after the db is set up.
Suppose I have a route like this:
app.get('/broken', (req, res) => {
throw new Error('Broken!');
});
This will never send a response to clients.
However, I can add a middleware for all errors:
const errorMiddleware = (error, req, res, next) => {
if (error) {
console.error(error);
return res.status(500)
.json({
message: 'Internal server error',
});
}
next(error);
};
But this will not work for async routes, because they do not throw directly.
For example, this will not work:
app.get('/broken', async (req, res) => {
throw new Error('Broken!');
});
So I can create a wrapper like this:
const asyncRoute = f => (req, res, next) => {
return Promise.resolve(f(req, res, next)).catch(next);
};
app.get('/broken', asyncRoute(async (req, res) => {
throw new Error('Broken!');
}));
But this is a real pain, because now I have to call this function for every route!
What is a better way of handling this?
The answer to Is there a way to wrap an await/async try/catch block to every function? is just what I describe above
The answer to how to use Promise with express in node.js? does not use await
Fundamentally, you don't want to directly pass an async function into Express's app.get, because app.get doesn't handle the promise the function returns. So you'll need to wrap those async handlers (as you're doing).
You can avoid having to do it every time by giving yourself a utility method at the top of the module:
const appGet = handler => app.get(asyncRoute(handler));
then use it instead of app.get:
appGet('/broken', async (req, res) => {
throw new Error('Broken!');
});
At some point (probably not right now), you might want to look at Koa.
Can one check a express.js route against multiple patterns? Consider the catch all * route below.req.route is matched to * here. I'd like to check the route against a few special scenarios within the same callback ~ NOT inside another all or use middleware.
app.all('*', (req, res, next) => {
// How do I check if route is a special case like below
if(req.route in ['/foo/:param', '/bar/:param']){}
})
I'm not sure why you're dismissing separate .all routes for this, because it seems to me to be the best way of performing these checks:
app.all('/foo/:param', (req, res, next) => {
req.isFoo = true;
next();
});
app.all('/bar/:param', (req, res, next) => {
req.isBar = true;
next();
});
app.all('*', (req, res, next) => {
if (req.isFoo || req.isBar) { ... }
})
Or, analogous to Chris's answer, have one route to match both:
app.all([ '/foo/:param', '/bar/:param' ], (req, res, next) => {
req.isSpecial = true;
next();
});
So you should not try to use the wildcard to capture everything than look for specific values. Instead, create an endpoint that looks for these specific values and then use another route for the capture all wildcard.
app.get(['/test', '/another_value'], (req, res, next) => {
})
Why does this give me 404-NotFound?
var test = require('./routes/test');
app.use('/test', test);
router.get('/test', function (req, res, next) {
//res.render('/test', { title: 'test' });
res.send('respond with a TEST resource');
});
where this given me what is expected?
var test = require('./routes/test');
app.use('/test', test);
router.get('/', function (req, res, next) {
//res.render('/test', { title: 'test' });
res.send('respond with a TEST resource');
});
In either case res.send() & res.render() behave alike. The first response is 404-NotFound. The second is what you want to see.
Thanks for the help
Is what I'm understanding correct?
The route.get('/', ... ) in this case really means the get of http://site/test because the test.js file is in a file routes/test.js.
So in this case the '\' of the get() is to relative to the root of /test.