Is modifying the request object in express.js bad practice? - javascript

I joined a small team of devs for a start up. We have not even launched yet. I have been handed a backend service written in node/express. I have not worked with this tech beyond small pet projects. I was looking into implementing a style guide just to keep code consistent, with the goal of implementing this across other backend services as well.
That brought me to the Airbnb style guide. This part jumped out at me.
Never mutate parameters
// bad
function f1(obj) {
obj.key = 1;
}
// good
function f2(obj) {
const key = Object.prototype.hasOwnProperty.call(obj, 'key') ? obj.key : 1;
}
In express there are typically controllers that get defined like so:
async function someController(req, res, next) {
// I've seen similar code to this
req.someNewProp = "Some new value."
res.status(200).json({"someJSONKey":"someJSONVal"});
}
Middleware typically gets defined like this:
// Route
router.get('/endpoint', function1, function2)
async function function1(req, res, next) {
// I've seen similar code to this
req.someNewProp = "Some new value."
// Pass req and res to function2
next();
}
I notice that the req object, as it gets passed around gets modified a lot. Data gets added to this object in middleware and other functions as it is passed along before the response is returned. The original dev that authored the code referred to it as "keeping things in request scope." But that seems to directly contradict a major point in the style guide and made me wonder if this is bad practice.
So the question now is, is there a "better" or more widely accepted way to keep track of things in the context of the request that is not mutating the original request object? What are some approaches of doing this?

Express provides a name space for applications to store request/response processing variables by adding them as properties of res.locals. This seems a better choice than attaching not standard properties to the request or response objects themselves.
In similar fashion, global application variables can be stored as properties of app.locals
Unfortunately there doesn't seem to be a locals property defined for router instances. I have placed a reference to global router instance options in res.locals as the first middleware step in a route I wrote, but that was my choice.
It can happen that request properties do need to be changed during processing, such as req.path, but this is not something to avoid at all costs. For example Express provides req.originalURL so you can recalculate path components any time you need to by deliberate design.
You may find Express gets more interesting with use - I've only recently learned of and passed an error object argument to the next function. As for the Airbnb guide quote in the post: underwhelming in a word! The "good" and "bad" code quoted in the post don't do the same thing.

It's not a bad practice, this is the idea behind the middleware in express, in the simple definition, middlewares are functions that can modify the request and response object or even decide if the flow of the request continue or it's terminated. However you have to be careful and don't set a value in a pre-existing property or you can have some strange behaviors, also if the information that you are going to store in the request in big, you can think in other strategies for instance store the information in a memory database as Redis.

After reviewing the express docs I found this bit in the middleware section:
Middleware functions can perform the following tasks:
Execute any code.
Make changes to the request and the response objects.
End the request-response cycle.
Call the next middleware function in the stack.
Middleware Docs
So it's probably safe to say that if the docs explicitly say that we can modify req and response objects in middleware, it is probably not bad practice.

Related

A little confused about var app= express()

I've been reading through the docs but still don't quite understand why we store express() inside an app variable.
I know we can't just call methods using express().get and .post because I tried and failed, but why?
How come it doesn't work like if we would call a function from the module.exports of any file we require?
I'm just really confused lol.
express expects you to create an instance object of it and use that. A short way of answering is to say "because that's what the makers of express expect from their users."
Across your script the expectation from the developers is that your .get and .post methods are called against a common instance of express. In this way, we can say that the call to express() initializes the instance and returns an object, which you store in app.
Edit in response to your comment:
express is a function that creates a new object based off a class
express() initializes the app object and I have not yet encountered a situation where I need to know specifically how. I have no idea if it's a function or a class. This is "encapsulation", the concept in OOP where there is a clear boundary between what you, the user of a module need to know in order to use it, and what the developer of the module needs to know to keep it working.
...dependent on the method used(ex: .get), and then uses that instance to allow us to make a route that returns things such as the req and res parameters in the callback?
The initialized object implements methods, callbacks, et al (like .get as you describe.)
All of which is in the express module?
All of which is the conventional pattern for working with the express API.
What really happens when your code call var express = require('express'), it actually imports the Factory Method called createApplication (source code here).
Meanwhile, when you do express().get and express().post, you're expecting that it will return the same instance of express app object, while it's not. Your code will work if express is using Singleton pattern under the hood (resulting in the same instance being returned on every call to express()). While the Factory Method design pattern will always create a new instance.
That said, every route you add directly using express().get or express().post will always be spread across many different application instance. So basically, it will work as advertised, but not as you expected to be.

Using server.inject in regular production use cases - bad idea or good idea?

Here's my default use case: I'm thinking about serving some of my REST resources over a socket.io layer instead of over http (since I end up needing to serve a lot of small api requests to render a typical page, and they're all over https, which has extra handshaking issues).
I'm still not sure this is a good idea in general (and have been looking at http2.0 as well). In the short term, I don't want to migrate off of hapijs or rewrite a ton of modular code, but I do want to try out making this work on some test servers to see how well it performs.
So I wrote a super-basic socket.io event handler that just takes requests off of the websocket event emitter and repackages them into hapi via a server.inject call:
module.exports = {
addSocket: function(sock) {
sock.on('rest:get:request', function(sock) {
return function(url) {
console.log(url);
hapi.inject({url: url, credentials: {user: sock.user}}, function(res) {
sock.emit('rest:get:response', url, res.payload);
});
};
})(sock);
}
};
So, all it really does is make sure the authentication object is set up (I have previously authenticated the user on the socket) and then inject a GET request to the server.
Usually, it seems like server.inject is used for testing, but this seems like a perfectly cromulent plan, unless (of course), it's super-slow or a bad idea for reasons I haven't foreseen. Hence: my question.
Server.inject is a great way of encapsulating sub requests however it can become more complicated than necessary though. A lighter approach would be to use shared handler functions and run them as pre-handlers.
What's nice about the pre handlers is that you can run them in parellel if needed.
One use case where I have used server.inject (other than in tests) is for a health check route. I'd have a route /health-check/db and /health-check/queue. I'd then have a /health-check route which encapsulated all of them. But again, this could have been completed with shared route handlers.
I had a lengthy discussion on the gitter channel the other day about this and my understanding is that it is neither good nor bad.
I guess a really good use case would be if you have multiple teams building plugins which expose routes and you want to use one of those routes in your plugin. You don't care about the implementation; you can just call the route.
Anothe reason for using server.inject is that it includes the validation steps provided by the route so you only need to have your resource defined in one place.

modifying res object in express.js

I had this idea for a backend design for my SPA-website. I would implement it so that routes starting with "/api/" would return only the relevant data in JSON. all other routes would lead to a full-page load.
Now my idea is to do this in middleware, as such:
app.use(function(req, res, next() ){
if(req.path.split("/")[0]=="api"){
res.send = res.json;
//or other custom response-function, I see many possibilities here!
}else{
...
}
});
app.use(routes);
now, my question is, does this modify the res object globally, or just for the current request (this instance of res)? My understanding is just the current one gets modified, and as far as I can tell thats true, but node is blazingly fast, so it's kinda hard to test on my own ( one can only refresh so many tabs in a millisecond! ) anyone know for sure?
Edit: A lot of the people answering my question have asked why I would want to do this. The point is to abstract what the server does for requests coming from clients with the front-end loaded, and clients who need the full page. I'm also considering the possibility of adding a route for loading partial templates, using this same method. by modifying res.send my controllers can worry about getting the data and send it of, res.send will already know if there needs to be some rendering involved. On second thought though res.send is really useful on its own, I might modify res to have res.answer or similar instead.(makes for less confusion too!)
I decided to make this an answer because sometimes future readers don't read the comments.
1) You can modify res and its members to your heart's content. You are operating on an instance of response, not its prototype. There is no "global" resource, but it does have a prototype.
2) Reading the documentation will pay off here. res.send operates identically to res.json if it is passed an object or array. Which is to say that the rest of your code will, in the typical case, run no differently than if you didn't monkey with res.send() but will confuse the heck out of someone (maybe you) several months or years later.
I tested this and each time a request came the response object had the original send function instead of the changed value. So no, it doesn't change it globally.
Here is the test I did:
app.use(function(req, res){
console.log(res.send);
res.send = 'a';
console.log(res.send);
});
Though I'm still not quite sure why you want to do this? It seems like it would be very confusing when someone looks at your API routes and sees res.send() but the effect instead is that of res.json. Why can't you use res.json in your API route functions?

Node.js and Express - is augmenting the request object a good idea?

i am starting to learn Node.js and trying to understand the architecture of it combined with the micro-framework Express.
I see that Express uses Connect as a middleware. Connect augments the request and response objects with all kinds of stuff in a chain of functions, and it provides an API so you can add custom middleware. I guess this augmenting is a way to keep things simple and flexible in the handlers/controllers, instead of having a variable number of parameters and parameter types. Here is an example of a simple GET handler:
app.get('/', function (req, res) {
res.render('index', { title: 'Hey', message: 'Hello there!'});
})
In tutorials from Node.js experts i have seen stuff like augmenting the request object with a MongoDB collection. In a blog from Azat Mardan i have seen this code :
var db = mongoskin.db('mongodb://#localhost:27017/test', {safe:true})
app.param('collectionName', function(req, res, next, collectionName){
req.collection = db.collection(collectionName)
return next()
})
The approach above is using the 'collectionName' parameter in the route name as a conditional to control the augmentation of the request. However, i have seen uglier code where the database middleware is attached on EVERY request that goes through Node.js without this conditional approach.
Looking at standard software principles like single responsibility principle, separation of concerns and testability why is it a good idea to extend the request with a MongoDB collection object and dozens of other objects? isn't the request and response object bloated with functionality this way and has unpredictable state and behavior? Where does this pattern come from and what are the pros and cons and alternatives?
This is fine. IMHO the very purpose of the request object is as a container to pass things down the stack for other handlers to use. It is far cleaner than looking for some agreed-upon-named global holder.
You could argue that it should be mostly empty, and then have the "official" request and response functionality on some property of the request/response objects, so it is cleaner, but I think the benefits are minimal.
Matter of fact, just about every middleware I have seen, including looking at the express source code and ones I have authored, uses request for exactly this sort of "container to pass properties and functionalities down the handler stack".

Middleware design pattern in Node.js: Connect

This question extends that of What is Node.js' Connect, Express and "middleware"?
I'm going the route of learning Javascript -> Node.js -> Connect -> Express -> ... in order to learn about using a modern web development stack. I have a background in low-layer networking, so getting up and going with Node.js' net and http modules was easy. The general pattern of using a server to route requests to different handlers seemed natural and intuitive.
Moving to Connect, I'm afraid I don't understand the paradigm and the general flow of data of this "middleware". For example, if I create some middleware for use with Connect ala;
// example.js
module.exports = function (opts) {
// ...
return function(req, res, next) {
// ...
next();
};
};
and "use" it in Connect via
var example = require('./example');
// ...
var server = connect.createServer();
// ...
server.use(example(some_paramater));
I don't know when my middleware gets called. Additionally, if I'm use()'ing other middlware, can I be guaranteed on the order in which the middleware is called? Furthuremore, I'm under the assumption the function next() is used to call the next (again, how do I establish an ordering?) middleware; however, no parameters (req, res, next) are passed. Are these parameters passed implicitly somehow?
I'm guessing that the collection of middleware modules used are strung together, starting with the http callback -> hence a bunch of functionality added in the middle of the initial request callback and the server ending a response.
I'm trying to understand the middleware paradigm, and the flow of information/execution.
Any help is greatly appreciated. Thank you for reading
The middleware is called as a chain of functions, with order based on middleware definition order(time) with matching routes (if applicable).
Taking in account that req and res objects are travelling through chain so you can reuse/improve/modify data in them along the chain.
There are two general use cases for middleware: generic and specific.
Generic is as you have defined in example above: app.use, it will apply to every single request. Each middleware have to call next() inside, if it wants to proceed to next middleware.
When you use app.get('/path', function(... this actual function is middleware as well, just inline defined. So it is sort of fully based on middlewares, and there is no endware :D
The chain order is based on definition order. So it is important to define middleware in sync manner or order-reliable async manner. Otherwise different order of middleware can break logic, when chain of middleware depends on each other.
Some middleware can be used to break the chain return next(new Error());. It is useful for example for validation or authentication middleware.
Another useful pattern of use for middleware is to process and parse request data, like cookies, or good example of such app.use(express.bodyParser());.

Categories