Is there a workaround for express 4.x route('/path') with params support? - express

I'm using a expressjs 4.x to build a simple api on top of mongodb.
The api needs to serve a few sets of data:
/api/v1/datatype1
/api/v1/datatype2
For each data type, I have CRUD operations (post, get, put, delete).
The api requests would look like this:
POST /api/v1/datatype1
GET /api/v1/datatype1:_id
PUT /api/v1/datatype1:_id
DELETE /api/v1/datatype1:_id
If I create a router params like this:
dataType1ApiRouter.param("entity_id", function (req, res, next, id) {
//async db fetch here by id, then call next with fetched data
//or error if faild request/not found entity.
//let's say req.dataEntity = dataEtity; next();
} );
If I create a route like this:
dataType1ApiRouter.route("/datatype1")
.get(":entity_id", function (req, res, next) {
//expcet req.dataEntity to be fetched by the param filter.
})
.post(function(req, res, next) {
//just create an new dataType1 entity.
});
I am getting a syntax error. The route .get and .post (and other methods like those) expect just one parameter, resulting in an error:
Route.get() requires callback functions but got a [object String]
Is there a way to actually group all the "/datatype1" requests under one url declaration instead of repeating the method("datatype1:entity_id") for each method that requires the ID expect for the post method?

There isn't a clean way to do this with Router.route(), but you might consider doing this with another Router instead of a Route there. Then, you could just mount that sub-router.
Basic example, modifying the code you provided:
var mainRouter = express.Router(),
subrouter = express.Router();
subrouter.param("entity_id", function (req, res, next, id) {
// param handler attached to subrouter
});
subrouter.post('/', function(req, res, next) {
// post handler attached to base mount-point
});
subrouter.get("/:entity_id", function (req, res, next) {
// get handler attached to base mount-point/<id>
});
// here we mount the sub-router at /datatype1 on the other router
mainRouter.use('/datatype1', subrouter);
Note that this requires adding a '/' to the URL, so instead of /api/v1/datatype1[someidhere] it would be /api/v1/datatype1/someidhere

Related

How do you access previous params in router file in express.js?

let's say I create a router like so in the app file:
const usernameRouter = express.Router();
app.use('/:username', usernameRouter);
When I'm in the router file, how would access that :username variable?
There is no built in way to get that parameter from the sub-route. You'd have several options:
1) Use req.originalUrl
In your sub-route handler, parse it out of req.originalUrl.
2) Move the /:username into the route declaration
Don't use a wildcard when sending to the router. Instead, just do this:
app.use(usernameRouter);
And, then inside of usernameRouter, do this:
router.get("/:username/something", ...);
So, you can then use req.params.username to get access to that.
3) Create middleware to capture req.params.username
Use a middleware function to set the parameter to a place you can get to it:
app.use('/:username', (req, res, next) => {
req.username = req.params.username;
usernameRouter(req, res, next);
});
Then, you can access it from req.username in the sub-routes.

When to use () in a middleware and when to use just reference

var cookieParser = require('cookie-parser')
var cookieValidator = require('./cookieValidator')
var app = express()
async function validateCookies (req, res, next) {
await cookieValidator(req.cookies)
next()
}
app.use(cookieParser())
app.use(validateCookies)
// error handler
app.use(function (err, req, res, next) {
res.status(400).send(err.message)
})
app.listen(3000)
In the above code both cookieParser() and validateCookies are middlewares but the way of executing them are different. There is a function like () with cookieParser but not with validateCookies. Can someone please explain why? Sorry If I am sounding foolish.
The app.use() function takes a single parameter, which is a function reference for a function that serves as a middleware for your router in Express.
You’ve correctly included the reference to your validateCookies function without parenthesis, as you just want Express to know which function you’d like it to use as middleware, and not to execute it at the time (Express will invoke the function for you when it’s time).
cookie-parser is a bit of an outlier (and I can see from where your confusion stems). The cookieParser() function actually returns a function reference upon successful execution. This design is likely because this particular module allows developers to pass in certain values to change the resulting function’s behavior as a middleware.

Express: How to properly define routes?

Suppose I have two routes defined like the following.
The first route is always executed, but the second one is not.
How should I define the routes, so that requests for /about.. are properly routed?
// First route
router.get('/:id', function (req, res) {
// This will always be executed
})
// Second route
router.get('/about/:name', function (req, res) {
// This will not be executed
})
Reverse the order
The routes are stored in a sequence in the order of your router.get() function calls. That is the order the routes are tested for a matching pattern. When you have a route that matches potentially everything, like an /:Id route, then you want to place it last. You then place the static non-changing ancillary pages before it.
In the example below I reverse the order so my static less specific route of "/about/" is checked first and if there is no match then express will compare the request to the next route for a URL match.
// Executed if match is found
router.get('/about/:name', function (req, res) {
})
// No match found on the above routes so try this one
router.get('/:id', function (req, res) {
})
//TODO: Good place for 404 handler...

Express: use separate route file for multiple kinds of http requests on same path

Here are my routes:
app.get('/signUp', routes.signUp);
app.post('/signUp' , routes.signUp);
Here is my separate file for routes.
exports.signUp = function(req, res) {
res.render('signUp');
};
The second block of code is behaviour I want in response to a get request.
How do I respond to a post request? I have already tied up the signUp function with behaviour that responds to get. Do I bundle up the post behaviour in the same function and render the sign up page again? Suppose I simply want to render the view, I don't want the post behaviour to execute in that case so it would be strange to bundle those together.
I believe the express router module should resolve this for you.
route file -
var express = require('express');
var router = express.Router();
router.route("/")
.get(function (req, res) {
res.render('signUp');
})
.post(function (req, res) {
//do something else
})
module.exports = router
index.js/app.js/server.js/whatever you call it.
//..
signUp = require("./routes/signup.js"); //or wherever this is
//...
app.use("/signUp", signUp);
//..

Pass request to every Swig template using Express

Is it possible to pass request/req object to every template I render, so I wouldn't have to pass it as local in every render() method?
You can use res.locals for that.
Insert the following middleware somewhere before your routes:
app.use(function(req, res, next) {
res.locals.req = req;
next();
});
This exposes req as a variable in your templates.