So, as the title suggests I am digging through the Express code to understand a little bit more about it.
I have been using the framework for a couple of years now and I've got used to writing things such as app.post(...), app.get(...)
Turns out there is no explicit declaration/definition of these methods within the Express code that is posted on Github. I guess they are implemented in an arcane, functional, obscure-to-JS-beginners way in there somewhere.
I would very much appreciate an explanation as to how this is done.
You can see on this line there's iteration over methods from npm package with the same name. This is the page of the package, it provides http method names handled by node http parser. Inside every app[method] function default router is initiated with this.lazyrouter (if it hasn't been already). Then a handler function is retrieved from router and called with arguments from the route.
Related
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.
I'm using a query on both server and client (pub/sub). So I have something like this at a few different locations.
const FOO = 'bar';
Collection.find({property:FOO})
Foo may potentially change and rather than have to update my code at separate locations, I was thinking it may be worth it to abstract this away to a global variable that is visible by both client and server.
I created a new file 'lib/constants.js' and simply did FOO = 'bar; (note no keyword). This seems to work just fine. I found this solution as the accepted answer How can I access constants in the lib/constants.js file in Meteor?
My question is if this a desired pattern in Meteor and even general JS.
I understand I can abstract this away into a module, but that may be overkill in this case. I also think using session/reactive vars is unsafe as it can kinda lead to action at a distance. I'm not even gonna consider using settings.json as that should only be for environment variables.
Any insights?
yes, If you are using older version of meteor then you can use setting.json but for updated version we have import option.
I don't think the pattern is that bad. I would put that file in /imports though and explicitly import it.
Alternatively, you can write into Meteor.settings.public from the server, e.g., on start-up, and those values will be available on the client in the same location. You can do this without having a settings file, which is nice because it doesn't require you to make any changes between development and production.
Server:
Meteor.startup(() => {
// code to run on server at startup
Meteor.settings.public.FOO = 'bar';
});
Client:
> console.log(Meteor.settings.public.FOO);
bar
This is actually a b̶a̶d̶ unfavoured pattern because with global variables you cannot track what things changed and in general constructing a modular and replaceable components is much better. This pattern was only made possible due to Meteor early days where imports directory/pattern was not supported yet and you'd have your entire code split up between both,server and client.
https://docs.meteor.com/changelog.html#v13220160415
You can find many write ups about it online and event stackoverflow answers so I don't want to restate the obvious.
Using a settings.json variable is not an option since we may dynamically change so what are our options? For me I'd say:
Store it the database and either publish it or retrieve it using methods with proper access scoping of course. Also you can dynamically modify it using methods that author DB changes.
Or, you may try using Meteor.EnvironmentVariable. I'd be lying if I said I know how to use it properly but I've seen it being used in couple Meteor projects to tackle a similar situation.
https://www.eventedmind.com/items/meteor-dynamic-scoping-with-environment-variables
Why are global variables considered bad practice?
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.
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?
I am struggling to really get a grasp on some fundamental basics here, and I feel it is not only holding me back, but resulting in crappy code and I don't like that.
I understand the concept of breaking out functional chunks of code into separate modules, like say routes, DB models, etc, but i'm having a real tough time understanding how to properly orchestrate the interdependent functioning of all these separate modules.
Let me give a fe examples of where my struggles lie.
Example 1
My ExpressJS 'app' is setup in my main program module, just like you see in every tutorial. However I need to access the app instance in other modules as well. How do I do that? One way I learned from various tutorials is to make the entire module export a function which takes the app as a param, then do what I need in the function. But that seems to me to add a lot of complexity to things. Not only do I now have an entire module enclosed in a function, but I seem to lose the ability to actually export multiple functions, objects, or other variables out of that module.
Module as a Function
module.exports = function(app) {
blah;
};
Module without a Function
exports.func1 = function() {
}
exports.func2 = function() {
}
The latter gives me much more flexibility in my eyes, but I seem to be forced often to use the former, because I need to pass in things like the app from somewhere else.
Example 2
I am using connect-rest for my REST API. All the code for my API lives in a separate module named simply 'api'. That has been fine until recently. Now I need to access a function that is in the api module, from inside my routes module. Presently my main routes are defined before my api, so I can't exactly pass my api export into my route function. I could reverse them probably, but this is only covering up a larger issue.
In short, the issue is one of increasing interdependence
As my codebase grows, i'm finding it more and more frequent that various modules need to work with each other - it just isn't feasible to keep them all completely searate. Sometime it is possible, but it is unclean.
I feel like i'm missing some basic Node.JS (or maybe just Javascript) paradigm that is used to manage all of this.
If anyone could help me understand I would be most appreciative. I am an experienced developer in other languages such as C++ and Python if it helps to couch things in other terms.
An attempt to sum up the issue
I feel that I did not adequately communicate my intention for posting, so let me try and sum up my issue with a working problem.
server.js
var http = require('http'),
express = require('express'),
app = express();
// Bunch of stuff done with app to get it set up
var routes = require('routes.js')(app);
app.js
module.exports = function(app, express) {
var router = express.router();
// code for routes
app.use('/', router);
}
In the above example, routes are split off into their own module, but that module needs app and express objects from server.js in order to function. So, by my current understanding, the only way to get those over into routes.js is to make routes.js export one big function which you then call with the two objects you need.
However, what if I want routes.js to export multiple functions that might be used in other places? By my understanding I now can't. What if I Wanted to do:
authentication.js
var routes = require('routes');
// setup auth
routes.doSomethingFunction(auth);
I can't do that because routes is only exporting that one mega function.
Each node module is simply an object. The part of that object which is available to the outside world is the module.exports object which contains properties which can be functions or data.
The require("xxx") command gets you the exports object for that module (from a central cache or loads it from the .js file is it hasn't yet been loaded).
So, code sharing is simple. Just have each module do a require() on any other modules that it wants to share code from and have those modules make sure the shared functions are accessible via it's own exports object. This allows each module to essentially be stand-alone. It loads any other code that it needs and makes it a lot easier to reuse code. Modules are cached so doing lots of require() operations on the same module from lots of other modules is nothing more than a cache lookup and is not something to worry about.
Data sharing (such as your app object) can be accomplished in several different ways. The most common way is when you load the module to just call some sort of initialization function for the module and pass it any data that it might need. That would be the push model. Or, you can also do the pull model where a module asks another module for some piece of data.
All of this is a lot easier with the right code organization. If you start to feel like you have a spaghetti or interdependence, then perhaps you don't have the right code organization or you're just a bit too shy on just using require() to pull in everything a given module needs. Remember each module will load whatever it needs itself so you only have to worry about what you need. Load those modules and they will load what they need.
You may also want to think more in terms of objects so you put most properties on some sort of object rather than lots of loose, individually shared variables. You can then share a single object and it automatically makes all the properties of that variable available to whomever you shared it with.
As to your question about sharing the app object with another module, you can do that like this:
// in your app module
var express = require('express');
var app = express();
var otherModule = require('otherModule');
otherModule.setApp(app);
// now otherModule has the singleton `app` object that it can use
// in this case, otherModule must be initialized this way before it can do its job
In this example, I just used a single method .setApp() to set the app object. That means all the other methods are available for other access into that module.
This could have also been done with a constructor-like method:
// in your app module
var express = require('express');
var app = express();
var otherModule = require('otherModule')(app);
This works also because the constructor can then return an object with other methods on it if you want. If you want to be able to get access to otherModule from within other modules, but obviously you only want to initialize it just once and not in those other places, then you can either do this:
var otherModule = require('otherModule')();
from those other modules and have the constructor just check that if nothing is passed to it, then it is not getting the app object from this constructor call so it should just return an object with other methods. Or, you can use the first code block above that returns all the methods from the initial require(). You're totally free to decide what to return from the require(). It can be just a constructor-like function which then returns another object when it is called. It can be just an object that has methods on it or (because functions are objects that can also have properties), you can even return a constructor-like function that ALSO has methods on it (though this is a bit less standard way of doing things).
And, your constructor function can decide what to do based on what is passed to it, given it a multitude of different behaviors based on what you pass to it.