Making req object available to every view file - node.js

I have a Node Express web project using Pug views.
By using the Response.locals object, I can make the Express Request object (req in my code) available to every pug file:
const app = require("express")();
app.use((req, res, next) => {
res.locals.req = req;
next();
});
Are there any side effects of using this approach and what are the disadvantages?
The convenience I get is that any view file can have access to all properties of the Request object, eg the query string, etc, without having to pass them explicitly using the textbook method like:
app.get("/xx", (req, res) => {
res.render("xx", { query: req.query });
});

Are there any side effects of using this and what are the disadvantages? Does it use up a lot of memory?
There are no side effects of using a template engine with express. Actually you can use res.render instead.
The convenience I get is that view files can have access to the query string,
If we know req.query has what we need that leaves the question is How to resolve the template for each route?
We will need to split the solution into two parts.
Part One - Genital request handle.
Although the response is dynamic the request resolving is known. Let say we have an additional parameter on the req object req.template and as we sad req.query is available as well.
The below function will render the req.query into req.template and send the response.
function pugTemplateHandler(req, res) {
const compiledFunction = pug.compileFile(req.template);
res.send(compiledFunction(req.query));
}
We don't care about Method nor Routes here. We expect that req to be set before this is called.
Part Two - Resolve the template file according to the route and method
Above we promised pugTemplateHandler that req will be ready for it. We can use Middleware to set the members we need on the req object.
app.get('...', (req, res, next) => {
req.template = 'PUG_TEAMPLATE_PAT'; // resolve template
// req.query = { ... }; // add or overwrite use params
next();
}, pugTemplateHandler); // pass modified req
app.post('...', (req, res, next) => { ... }, pugTemplateHandler);
app.put('...', (req, res, next) => { ... }, pugTemplateHandler);
app.del('...', (req, res, next) => { ... }, pugTemplateHandler);
Because we know the HTTP Method and the Route resolving the template is easy. Most likely here the template will be a static string.
The solution is extendable and has respect the idea of separation of concerns.

Related

first portion of route url is not included

I have a route where I built two GET APIs. I would like one to redirect from /download to /zip all while passing a parameter. The problem is I am getting a 404 for some reason the routes url is not being included in the redirect()
Here are the APIs.
// respond with xml from from static folder
router.get('/zip/:id', function (req, res) {
fileName = req.params.id
});
router.get('/download', function (req, res, next) {
var id = req.query.id
res.redirect('/zip?id='+ id);
});
module.exports = router;
I get a 404 when testing the URL:
localhost:8000/rest/pluto/v1/plugin/download?id=networktool
I am thinking it might be how I have the middleware setup but not real sure. I'm still new to node/express.
You are redirecting to a route that isn't actually defined. With your /zip/:id route definition:
router.get('/zip/:id', function (req, res) {
var fileName = req.params.id
});
The way that is defined, you have to have id information in the URL itself, so while the following routes would work:
/zip/networktool
/zip/1234
these routes would not:
/zip
/zip?id=networktool
/zip?id=1234
because Express is looking for the id to be built into the route itself. So you can do one of two things. You can either change your redirect to:
router.get('/download', function (req, res, next) {
res.redirect('/zip/'+ req.query.id);
});
or, you can modify your /zip route to make the id parameter optional with ?:
router.get('/zip/:id?', function (req, res) {
var fileName = (req.params.id) ? req.params.id : req.query.id;
});
I would recommend the first option, as the latter optional parameter only makes your zip route more complicated and require extra handling of whether id is actually passed to your route.
The path /zip/:id is expecting a path parameter not a query parameter.
You should redirect like this
res.redirect('/zip/'+ id);

req.query undefined in Nuxt Express Middleware get request

Not a pro with things like server middleware, but stuck without much clues.
In a vue component I am retrieving data with axios with this:
axios
.get('/api/getDailyFeedback', {
params: {
start: '2018-05-01'
}
})
Which goes to the express (version 4.16.2) server middleware setup in Nuxt. I have gotten normal get and post requests to work fine but the problem is when I want to pass in a parameter into a get request like the below:
router.get('/getDailyFeedback', (req, res) => {
console.log('Query:', req.query.start);
//do stuff
});
What little experience I had with Express 4, it took me a little bit of time to realise why parameters passed in the body of a post request were undefined, but it was because I needed to add body-parser.json() to my nuxt config since they removed that from the main express package. Similarly I thought I needed bodyParse.urlencoded but that has not worked. This is the line I added to my nuxt.config:
serverMiddleware: [bodyParser.urlencoded({ extended: false }), bodyParser.json(), '~/api'],
I am unsure if the content type is not set correctly or I am missing something simple here. I know I am able to use various libraries to grab the parameters from the url string which I have access to, as Axios is working as expected and adding my 'params' object onto the end of my get request. But a lot of solutions I have seen to other problems is 'oh its as simple as accessing req.query' Alas Express fails to define a req.query object, or even req.params or req.body.
Hopefully that is enough detail to go on, thanks a lot for reading and any suggestions.
Well that's an unpleasant surprise to try to use the get request in Nuxt for the first time, isn't it? Nuxt creates a connect instance which leads to some pitfalls. It's req is the Node.js http request object, so it doesn't have the query property and request.body is also being skipped in get requests!
But there is a way for everything. Your code:
axios.get('/api/getDailyFeedback', {
params: {
start: '2018-05-01'
}
})
Translates to the following URI call:
/api/getDailyFeedback?start=2018-05-01
So you've got all the params in the URI where you can retrieve them from via url parsing. Module url ships with Node.js by the way.
const url = require("url");
router.get('/getDailyFeedback', (req, res) => {
let queryData = url.parse(req.url, true).query
console.log('Query:', queryData.start)
});
I wrote a little helper that extends req with a custom property. Here's the code:
router.use((req, res, next) => {
var theQuery = url.parse(req.url, true).query
req.queryData = theQuery
next()
})
Then you can use it like this:
router.get('/getDailyFeedback', (req, res) => {
console.log('Query:', req.queryData.start)
});
Other way to pass params via get is by using optional uri segments:
router.get('/getDailyFeedback/:start?', (req, res) => {
console.log('Query:', req.params.start)
});
I am using Nuxt 2.15 and I can access the query parameter like this req._parsedOriginalUrl.query
You can access Nuxt get params without additional modules.
Just use:
req.request.get()
For anyone else in the future
const { Router } = require('express')
Router.get('/orders', async (req, res, next) => {
const request = req.query.sometext;
res.json({data: request});
});
module.exports = Router;
<script>
export default() {
async mounted() {
const orders = await axios.get(`/api/orders`, {
params: {
sometext: 'bla bla'
}
});
}
}
</script>
http://expressjs.com/en/api.html#req

Express 4: router syntax

I am using Express 4 with the new router. At least one thing continues to confuse me, and it is a syntax problem - I am wondering if there is a regex that can do what I want. I have a standard REST api, but I want to add batch updates, so that I can send all the info to update some users models with one request, instead of one PUT request per user, for example. Anyway, I currently route all requests to the users resources, like so:
app.use('/users, userRoutes);
in userRoutes.js:
router.get('/', function (req, res, next) {
//gets all users
});
router.put('/:user_id', function (req, res, next) {
//updates a single user
});
but now I want a route that captures a batch request, something like this:
router.put('/Batch', function (req, res, next) {
//this picks up an array of users from the JSON in req.body and updates all
});
in other words, I want something which translates to:
app.use('/usersBatch, function(req,res,next){
}
...but with the new router. I can't get the syntax right.
I tried this:
app.use('/users*, userRoutes);
but that doesn't work. Does anyone know how to design this?
I'm guessing that the call to [PUT] /users/Batch is being picked up by the [PUT] /users/:user_id route. The string /:user_id is used as a regular expression causing it to also collect /Batch.
You can either move /Batch before /:user_id in the route order, refine the regex of /:user_id to not catch /Batch or change /Batch to something that won't get picked up too early.
(plus all the stuff Michael said)
REST doesn't include a POST as a list syntax. That's because each URL in REST point to an individual resource.
As an internet engineer I haven't seen any bulk PUTs or POSTs, but that said, it's your app, so you can make whatever API you like. There are definitely use cases for it.
You'll still need to describe it to Express. I would do it like this:
var express = require('express');
var router = express.Router();
router.get('/', function (req, res) {}); // gets all users
router.post('/:user_id', function (req, res) {}); // one user
router.put('/:user_id', function (req, res) {}); // one user
router.patch('/:user_id', function (req, res) {}); // one user
router.delete('/:user_id', function (req, res) {}); // one user
app.use('/user', router); // Notice the /user/:user_id is *singular*
var pluralRouter = express.Router();
pluralRouter.post('/', function (req, res) {
// req.body is an array. Process this array with a foreach
// using more or less the same code you used in router.post()
});
pluralRouter.put('/', function (req, res) {
// req.body is another array. Have items in the array include
// their unique ID's. Process this array with a foreach
// using +/- the same code in router.put()
});
app.use('/users', pluralRouter); // Notice the PUT /users/ is *plural*
There are other ways to do this. Including putting comma-delimited parameters in the URL. E.g.
GET /user/1,2,3,4
But this isn't that awesome to use, and vague in a PUT or POST. Parallel arrays are evil.
All in all, it's a niche use case, so do what works best. Remember, the server is built to serve the client. Figure out what the client needs and serve.

How to implement HMVC structure in Node/Express

I have several "api" endpoints in my application that are used by the front-end framework for its AJAX processes. For organizational purposes, I would like to re-use the code for these endpoints to retrieve data for server-side rendering, which I do in some instances to play nicely with search engines. So, ideally I would implement some sort of HMVC setup to simply access a route on the API endpoint. Specifically I would like to get the response and perform additional actions on it before issuing the top-level response (e.g., render a view with results).
For example:
app.get('/post/recent', function(req, res) {
app.doRequest('/api/posts/', req, function(res2) {
var data = res2.body;
res.render('posts/index', data);
});
});
What's the best way to do this?
So far the best option I've come up with is:
Expose all logic in an endpoint as a method, which would be used in app.get('...', myFunction), and then I could call myFunction elsewhere outside of the express flow for that path. However, this would not give me a reliable way to run middleware specific to the endpoint (which I would also want to run on the HMVC request) unless I wrote my own middleware implementation that did not rely on express. The API endpoint has middleware that does something like if(!hasAccess) res.send(403), which I specifically do NOT want to happen in my main route since I'd want to render a nice error page instead of just sending an error code.
Example:
var getPosts = function(req) {
var deferred = q.defer()
doDatabaseQuery(req.query).then(function(response) {
deferred.resolve(response)
});
};
app.get('/api/posts', myMiddlewareFunction(), function(req, res) {
getPosts(req).then(function(response) {
res.send(response);
});
);
app.get('/post/recent', function(req, res) {
// I want to run middleware here, not in root level
getPosts(req).then(function(response) {
res.render('post/index', response);
}, function(err) {
res.render('error/noaccess');
});
});
Any better ideas? Is there a way to programmatically access an express route and get the result?
I figured this out by diving into the Express source code. There is a method called handle which I can manually invoke with modified request and response objects to get the effect I want:
app.get '/posts', (req, res) ->
req.url = '/api/posts'
newRes = _.clone(res)
newRes.send = (data, code)->
if code is 200
return res.render('posts/index', data)
else
return res.render('error')
app.handle(req, newRes)

Express, multiple routes to same function

I am mimicking another api. I would also like to provide a different (better IMHO) api as well.
// this is url I need to support
api.post('/books/updateBook', function(req, res) {
...
});
// Would also like to support
api.put('/books/:bookId', function(req, res) {
...
});
I could easily do:
var updateBook = function(req, res) {
...
}
// this is url I need to support
api.post('/books/updateBook', updateBook);
// Would also like to support
api.put('/books/:bookId', updateBook);
Perfectly acceptable right? Being new to express I am wondering if there is a more 'express' way to handle this. I know you can use regex, but I am not sure you can map regex across different HTTP verbs (POST vs PUT).
Thoughts?
api.all('/books/:bookId', function (req, res, next) {
if (req.method === 'PUT' || req.method === 'POST) {
//get your groove on
} else {
next();
}
});
You can combine verbs in express, you just use all and examine the method, if it matches, handle the request other wise pass it down the handler chain (with next();).
That being said I think you're doing it right, there's no reason route handlers need to be lamdas.

Resources