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.
Related
I have an async function as a route handler, and i'd like to have errors handled as some kind of middleware. Here is my working attempt:
router.get(
"/",
asyncMiddleware(
routeProviderMiddleware(
async ({ y }) => ({
body: await db.query({x: y})
})
)
)
)
// This is the middleware that catches any errors from the business logic and calls next to render the error page
const asyncMiddleware = fn =>
(req, res, next) => {
Promise.resolve(fn(req, res, next))
.catch(next)
}
// This is a middleware that provides the route handler with the query and maybe some other services that I don't want the route handler to explicitly access to
const routeProviderMiddleware = routeHandlerFn => async (req, res) => {
const {status = 200, body = {}} = await routeHandlerFn(req.query)
res.status(status).json(body)
}
What I strive to is a way to make the route declaration cleaner - I don't want the 2 middleware wrappers there, ideally i'd like for the business logic function there only, and somehow declare that every route is wrapped in these.
Even combining the two middlewares together would be nice, but I didn't manage.
I use following approach:
Create asyncWrap as helper middleware:
const asyncWrap = fn =>
function asyncUtilWrap (req, res, next, ...args) {
const fnReturn = fn(req, res, next, ...args)
return Promise.resolve(fnReturn).catch(next)
}
module.exports = asyncWrap
All your routes/middlewares/controllers should use this asyncWrap to handle errors:
router.get('/', asyncWrap(async (req, res, next) => {
let result = await db.query({x: y})
res.send(result)
}));
At app.js, the last middleware will receive the errors of all asyncWrap:
// 500 Internal Errors
app.use((err, req, res, next) => {
res.status(err.status || 500)
res.send({
message: err.message,
errors: err.errors,
})
})
Express 5 automatically handles async errors correctly
https://expressjs.com/en/guide/error-handling.html currently says it clearly:
Starting with Express 5, route handlers and middleware that return a Promise will call next(value) automatically when they reject or throw an error. For example:
app.get('/user/:id', async function (req, res, next) {
var user = await getUserById(req.params.id)
res.send(user)
})
If getUserById throws an error or rejects, next will be called with either the thrown error or the rejected value. If no rejected value is provided, next will be called with a default Error object provided by the Express router.
I have shown that in an experiment at: Passing in Async functions to Node.js Express.js router
This means that you will be able to just make the callback async and use await from it directly without any extra wrappers:
router.get("/", async (req, res) =>
const obj = await db.query({x: req.params.id})
// Use obj normally.
)
and errors will be correctly handled automatically.
Express permits a list of middlewares for a route and this approach sometimes works for me better than higher-order functions (they sometimes look like an overengineering).
Example:
app.get('/',
validate,
process,
serveJson)
function validate(req, res, next) {
const query = req.query;
if (isEmpty(query)) {
return res.status(400).end();
}
res.locals.y = query;
next();
}
function process(req, res, next) {
Promise.resolve()
.then(async () => {
res.locals.data = await db.query({x: res.locals.y});
next();
})
.catch((err) =>
res.status(503).end()
);
}
function serveJson(req, res, next) {
res.status(200).json(res.locals.data);
}
What you can do is add an error handlers after your routes. https://expressjs.com/en/guide/error-handling.html
app.use(function (err, req, res, next) {
console.error(err.stack)
res.status(500).send('Something broke!')
})
What I ended up doing is unifying the wrappers like this:
const routeProvider = routeHandlerFn => async (req, res, next) => {
try {
const {status = 200, body = {}} = await routeHandlerFn(req.query)
res.status(status).json(body)
} catch(error) {
next(error)
}
}
This wrapper is all any route would need. It catches unexpected errors and provides the route handler with the needed params.
In express.js, if I have this route in my server side
router.get('/ad/:id', (req, res) => {
const { id } = req.params
Ad.getAd(id, (err, resp) => {
if(err){
return handleError('Failed to load an ad', res)
}
res.json({
success: true,
result: resp
})
})
})
and it worked fine I want to load a detail ad like example.com/ad/123 where id is 123. But I can't do example.com/ad/create anymore, any way to check the type of the param?
You can create separate route for it. Place before /ad/:id route, because it's catching all requests like /ad/*.
router.get('/ad/create', (req, res) => { /* some work */ })
router.get('/ad/:id', (req, res) => { /* some work */ })
Since you mentioned you are building a SPA, you must redirect all GET requests to react-router:
app.get("*", function (req, res) {
res.sendFile(__dirname + "/path/to/index.html")
})
Also you can prepend api to all back-end endpoints to prevent ambiguity.
router.get('/api/ad/create', (req, res) => { /* some work */ })
router.get('/api/ad/:id', (req, res) => { /* some work */ })
router.get('/ad/:id', (req, res,next) => {
const { id } = req.params
if(! parseInt(id,10)){
return next();//skip this route if not a number
}
//its a number
});
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) => {
})
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.
I'm trying to use an array of middlewares. Well, more like a combination of function names and arrays.
Instead of having:
router.post('/editPassword', validate, changePassword, sendConfirmation);
I would like to have something like:
router.post('/editPassword', validate, [changePassword, sendConfirmation] );
That would look like:
router.post('/editPassword', validate, doAction );
Where doAction would be an array like this:
var doAction = [
//equivalent of changePassword
function(req, res, next){
//whatever
next();
},
//equivalent to the previous sendConfirmation
function(req, res, next){
//whatever
}
]
But it seems it is failing and going back to the validate step after the next() within the first function in doAction.
I'm looking for a way to simplify the middleware chaining including some middleware steps under the same name.
Latest version of Express can handle this:
function logOriginalUrl (req, res, next) {
console.log('Request URL:', req.originalUrl)
next()
}
function logMethod (req, res, next) {
console.log('Request Type:', req.method)
next()
}
var logStuff = [logOriginalUrl, logMethod]
app.get('/user/:id', logStuff, function (req, res, next) {
res.send('User Info')
})
You can review more from this link
I assume the reason you wanted it to look that way is not only for it to appear presentable, but also to be able to reuse the other middleware. In that case, you can create a middleware which runs all other middlewares to do the check for you, and only calls the next function if all validations succeed.
var express = require('express');
var app = express();
function middleware1(req, res, next) {
if(req.query.num >= 1) {
next();
} else {
res.json({message: "failed validation 1"});
}
}
function middleware2(req, res, next) {
if(req.query.num >= 2) {
next();
} else {
res.json({message: "failed validation 2"});
}
}
function middleware3(req, res, next) {
if(req.query.num >= 3) {
next();
} else {
res.json({message: "failed validation 3"});
}
}
function combination(req, res, next) {
middleware1(req, res, function () {
middleware2(req, res, function () {
middleware3(req, res, function () {
next();
})
})
})
}
app.get('/', combination, function (req, res) {
res.send('Passed All Validation!');
})
app.listen(3000, function () {
console.log('Example app listening on port 3000!')
})
You can test this app by running it then viewing http://localhost:3000/?num=3, changing the value 3 to a lower number, or removing the num parameter.
I'm not sure if this is the proper way to do it, but this is how I've handled my other projects. Let me know what you think.
note: see comments for use case. #robertklep may have a better solution depending on how you want to use middlewares
Just search a little more ^^ : Less ugly and more understandable than previous answer
https://github.com/blakeembrey/compose-middleware
Be careful that you're not doing (the equivalent of) this in your validate middleware:
function middleware(req, res, next) {
if (someCondition) {
console.log('some condition is true');
next();
}
console.log('some condition is false');
res.status(400).end();
}
The intention here is that after calling next the rest of the code isn't executed, but it will. There's nothing really special about next, so when you call it, after it returns the middleware code continues to run (causing both "some condition is true" and "some condition is false" to be logged).
That's why you often see this:
if (someCondition) {
console.log('some condition is true');
return next();
// Or, alternatively:
// next();
// return;
}
The return causes the middleware function to return after calling next, so the rest of the code in the function won't be executed.
This functionality is already built into express as an array or middleware:
let combined = express.Router()
.use(
[
middleware1,
middleware2,
middleware3,
],
);
let combined = express.Router()
.use(
middleware1,
middleware2,
middleware3,
);
Full Example
"use strict";
let Http = require("http");
let Express = require("express");
let server = Express();
let app = Express.Router();
let combined = Express.Router();
combined.use(
function (req, res, next) {
console.log("huzzah!");
next();
},
function (req, res, next) {
res.json({ success: true });
}
);
function middleware0(req, res, next) {
console.log('ground zero');
next();
}
app.get("/combined", middleware0, combined);
server.use("/", app);
Http.createServer(server).listen(3000);