I am trying to understand and find out the better way to use modules in node js that are imported via require.
Here are two possible ways I've found.
class BookManager {
constructor() {
this._validator = require('validator');
this._BookModel = require('api/book').BookModel;
this.createNewBook = this.createNewBook.bind(this);
}
}
And
const validator = require('validator');
const BookModel = require('api/book').BookModel;
class BookManager {
constructor() {
this.createNewBook = this.createNewBook.bind(this);
}
}
In the first case we are encapsulating all neccessary objects as members of the class. From my previous experience working with other languages, this could probably replaced with the dependency injection via constructor.
But encapsulating a class definition seems a bit weird for me. So it could also be like that
const BookModel = require('api/book').BookModel;
class BookManager {
constructor() {
this._validator = require('validator');
this.createNewBook = this.createNewBook.bind(this);
}
}
But I am not sure in case of Node JS which way is preferable and why.
Could someone explain the differences, especially about scopes.
thanks.
Related
I want a JavaScript class that can conditionally add additional methods into itself from separate files. The idea is to separate different concerns of the app into more manageable self-contained modules that nevertheless can interact with the methods in the mother app class. Therefore, the additional methods in the separate file must be able to reference the methods and variables in the main class. See the code sample below.
I looked at a lot of different solutions, but all of them have a downside for what I'm wanting.
I could do new Uploads(), but I could not find a way for methods in Uploads.js to reference methods in the main App class
I could use extends, but this would not allow for conditional extension AFAIK
I could just define new methods into the prototype itself, but this would mean that the external file would need to "know" about the class it's going to be used in, which doesn't make it widely reusable.
The best I have so far is the following:
app.js
const Uploads = require("./Uploads.js");
const config = { hasUploads: true }; // Probably loaded from a file
class App {
constructor() {
/* Only include the separate module if the config says so */
if(config.hasUploads) {
Object.assign(this, Uploads);
}
}
foo() {
/* Something */
}
}
Uploads.js
module.exports = {
bar() {
this.foo();
}
};
It works, but I don't know if this is the best solution;
There's no constructor, so if Uploads.js needs to do some setup, app.js needs to contain the logic to do so (or at least know to call some uniquely named faux constructor method), which doesn't seem ideal.
If Uploads.js contains a method with the same name as app.js or any other possible module being loaded, they will be overwritten, leading into unexpected behaviour.
The Uploads.js is an object of functions, whereas app.js defines a class. Ideally (though I guess not necessarily) for code manageability they should both use the same syntax.
Is there a better/cleaner/nicer way of doing this?
Instead of trying to perform some kind of crazy multi inheritance, why not try embracing composition? Its very good for solving these kinds of problems.
class App {
constructor(modules) {
if (modules.uploads) {
this.uploads = modules.uploads(this);
}
}
foo() {
console.log('foo!');
}
}
class Uploads {
constructor(context) {
this.context = context;
}
method() {
this.context.foo();
}
}
const app = new App({ uploads: (ctx) => new Uploads(ctx) });
app.uploads.method();
You can get really fancy with this and use builders to configure apps with specific types of modules.
Depending on your anticipated complexity, you might want to think about using event buses, mediators, or commands to decouple things from the host itself.
One option to fix overwriting an existing method from the uploads file is to assign new methods in a loop and check for duplicates (Object.assign is not ideal in this case) and only add updates once:
const Uploads = {
bar() {
this.foo("called from bar");
}
};
const config = { hasUploads: true, // Probably loaded from a file
configured: false
};
class App {
constructor() {
/* Only include the separate module if the config says so */
if(config.hasUploads && !config.configured) {
const proto = this.constructor.prototype;
const methods = Object.keys(Uploads);
methods.forEach( name=> {
if( proto[ name] ) {
throw new Error( "App already has method " + name);
}
proto[name] = Uploads[name];
});
config.configured = true;
}
}
foo(arg) {
/* Something */
console.log( arg );
}
}
const app = new App();
app.bar();
A better (cleaner) alternative might be to add updates to the class before calling its constructor, using a static class method because its this value is the constructor function. Tested example:
static addMethods(uploads) { // inside class declaration
const proto = this.prototype;
for (const [name, method] of Object.entries(uploads)) {
if( proto[name]) {
throw new Error("App already has a ${name} method");
}
proto[name] = method;
}
}
to be called as needed by
if( config.hasUploads) {
App.addMethods( Uploads);
}
I've got a Tag class and a TagCollection class to store multiple tags. Now the TagRepository class needs to construct a new Tag object to return. But when I try to create a new Tag inside the TagRepository it returns an error:
Tag is not defined
This is how I include all the classes in the main JavaScript files:
const Tag = require('./class/Tag.js');
const TagCollection = require('./class/TagCollection.js');
const TagRepository = require('./repository/TagRepository.js');
How do we usually deal with this? I could just include the needed classes inside the constructor of the class that requires them. But that seems messy when I have to include multiple classes.
The only other solution I could think of is making the needed classes global, reading online it seems like that is considered bad practice. Below I've included all the classes
Tag.js
module.exports = class Tag {
constructor() {
this.id;
this.name;
}
setId(id) {
this.id = id;
}
setName(name) {
this.name = name;
}
getId() {
return this.id;
}
getName() {
return this.name;
}
}
TagCollection.js
module.exports = class TagCollection {
constructor() {
this.tags = [];
}
addTag(tag) {
this.tags.push(tag);
}
setTags(tags) {
this.tags = tags;
}
getTags(tags) {
return this.tags;
}
}
TagRepository.js
module.exports = class TagRepository {
constructor(conn) {
this.conn = conn;
}
getAll(callback) {
let tempTagCollection = new TagCollection;
this.conn.query(`SELECT \`id\`, \`name\` FROM \`tag\` WHERE 1`, function (error, tags) {
tags.forEach((tag) => {
//Create single tag
let tempTag = new Tag;
//Set properties
tempTag.setName(tag.name);
tempTag.setId(tag.id);
//Add single tag to collection
tempTagCollection.addTag(tempTag);
})
callback(tempTagCollection);
})
}
}
The only other solution I could think of is making the needed classes global, reading online it seems like that is considered bad practice.
You're right, making global variables should be avoided as much as possible as it promotes brittle and hard to debug code.
You can think of each file as a module. I personally like to keep one file per class, so I can treat the class itself as a module. In each module, you should require every class that you depend on.
So I'll use a classic Animal/Cat/Dog example:
//Animal.js
module.exports = class Animal { ... }
//Cat.js
const Animal = require('./Animal');
class Cat extends Animal { ... }
//Dog
const Animal = require('./Dog');
class Dog extends Animal { ... }
In NodeJS, even though both Cat & Dog requires Animal, Animal.js is only ever executed once. So each module that requires Animal will obtain the same Animal class.
I could just include the needed classes inside the constructor of the class that requires them.
I would also avoid doing this. Using require in a constructor, even though a require'd file will only execute the file the first time it has been required, it still goes through the node file resolution algorithm which is an expensive process and may cause performance bottlenecks. Generally it is best to have your require statements outside of constructors or functions. Keep them at the top of the file, where all the requires will run once when the application loads.
As you can now see inside the TagRepository it requires two classes, the Tag and TagCollection class how would I go about this?
Inside TagRepository.js you just need to simply have 2 include statements, one for each file, see below.
const Tag = require('./Tag');
const TagCollection = require('./TagCollection.js');
// Both Tag and TagCollection is now usable
class TagRepository { ... }
Further reading on NodeJS modules can be found here
https://nodejs.org/dist/latest-v10.x/docs/api/modules.html#modules_modules
You need to 'require' every class you need in every file you need it, so unlike (for example) PHP you can't just require everything once per program.
I am confused while encountering this over-looked question like how they are different ?
Module.exports = {
search_companies(req, res) {
//some ops
},
get_details(req, res) {
//some ops
}
};
vs
class MainContrller {
search_companies(req, res) {
//some ops
},
get_details(req, res) {
//some ops
}
}
module.exports.MainController = MainController;
The first one exports an object with the function search_companies and get_details. So you can call these to function on the object that is exported.
The second one exports a class MainController with the functions search_companies and get_details. Here you have to create an instance of MainController to be able to call those two functions on the instance.
You use the first syntax if you only need one instance of that object through the whole project. It's like a singleton or like static, but without the need to define an actual class for it.
And you use the second one if you need multiple different instances of MainController.
A module is supposed to be used like:
const { search_companies } = require('...');
A class is supposed to be used like:
const { MainController } = require('...');
const { search_companies } = new MainController();
MainController in this case is a bad practice because it mimics the usage of classes in other languages without taking the specifics of JavaScript into account.
MainController doesn't benefit from being a class if this instance is ignored and doesn't lose in functionality when a class is refactored to separate functions.
Classes aren't glorified namespaces in JavaScript; there are modules that already serve this purpose. If there's a need for a namespace and no need for class instance, a module can be used as a rule of thumb.
Suppose a file has a lot prototypes and function object declarations code:
function Sample() {
...
}
Sample.prototype.method1 = () => {
...
}
Sample.prototype.method2 = () => {
...
}
When do I need to export this main object using? Example:
module.exports = new Sample;
// or
module.exports = Sample;
I have a few questions about this:
What signals of the project design will let me decide to use one or the other?
Does have these methods any disadvantages on using one of them (performance, prototyping inheritance)?
Can you provide a short example/s on when is it good practice to use module.exports = new Sample and when module.exports = Sample?
It depends on whether the Sample is ever going to be instantiated more than once. If it is going to be instantiated more than once by whatever consumes it, then you absolutely have to export Sample the class itself.
Otherwise, you could instantiate a Sample and export it, ensuring that there will only ever be one instantiated Sample object:
module.exports = new Sample();
Make sure to use those parentheses - you're invoking a constructor, after all.
But at this point, there isn't that much much point making a class at all - why not export a plain object instead?
I'm new to NodeJs development coming from the .NET world
i'm searching the web for best practices regrading DI / DIP in Javascript
In .NET i would declare my dependencies at the constructor whereas in javascript i see a common pattern is to declare dependencies in the module level via a require statement.
for me it looks like that when i use require i'm coupled to a specific file while using a constructor to receive my dependency is more flexible.
What would you recommend doing as a best practice in javascript? (I'm looking for the architectural pattern and not an IOC technical solution)
searching the web i came along this blog post (which has some very interesting discussion in the comments):
https://blog.risingstack.com/dependency-injection-in-node-js/
it summerizes my conflict pretty good.
here's some code from the blog post to make you understand what i'm talking about:
// team.js
var User = require('./user');
function getTeam(teamId) {
return User.find({teamId: teamId});
}
module.exports.getTeam = getTeam;
A simple test would look something like this:
// team.spec.js
var Team = require('./team');
var User = require('./user');
describe('Team', function() {
it('#getTeam', function* () {
var users = [{id: 1, id: 2}];
this.sandbox.stub(User, 'find', function() {
return Promise.resolve(users);
});
var team = yield team.getTeam();
expect(team).to.eql(users);
});
});
VS DI:
// team.js
function Team(options) {
this.options = options;
}
Team.prototype.getTeam = function(teamId) {
return this.options.User.find({teamId: teamId})
}
function create(options) {
return new Team(options);
}
test:
// team.spec.js
var Team = require('./team');
describe('Team', function() {
it('#getTeam', function* () {
var users = [{id: 1, id: 2}];
var fakeUser = {
find: function() {
return Promise.resolve(users);
}
};
var team = Team.create({
User: fakeUser
});
var team = yield team.getTeam();
expect(team).to.eql(users);
});
});
Regarding your question: I don't think that there is a common practice in the JS community. I've seen both types in the wild, require modifications (like rewire or proxyquire) and constructor injection (often using a dedicated DI container). However, personally, I think not using a DI container is a better fit with JS. And that's because JS is a dynamic language with functions as first-class citizens. Let me explain that:
Using DI containers enforce constructor injection for everything. It creates a huge configuration overhead for two main reasons:
Providing mocks in unit tests
Creating abstract components that know nothing about their environment
Regarding the first argument: I would not adjust my code just for my unit tests. If it makes your code cleaner, simpler, more versatile and less error-prone, then go for out. But if your only reason is your unit test, I would not take the trade-off. You can get pretty far with require modifications and monkey patching. And if you find yourself writing too many mocks, you should probably not write a unit test at all, but an integration test. Eric Elliott has written a great article about this problem.
Regarding the second argument: This is a valid argument. If you want to create a component that only cares about an interface, but not about the actual implementation, I would opt for a simple constructor injection. However, since JS does not force you to use classes for everything, why not just use functions?
In functional programming, separating stateful IO from actual processing is a common paradigm. For instance, if you're writing code that is supposed to count file types in a folder, one could write this (especially when he/she is coming from a language that enforce classes everywhere):
const fs = require("fs");
class FileTypeCounter {
countFileTypes(dirname, callback) {
fs.readdir(dirname, function (err) {
if (err) return callback(err);
// recursively walk all folders and count file types
// ...
callback(null, fileTypes);
});
}
}
Now if you want to test that, you need to change your code in order to inject a fake fs module:
class FileTypeCounter {
constructor(fs) {
this.fs = fs;
}
countFileTypes(dirname, callback) {
this.fs.readdir(dirname, function (err) {
// ...
});
}
}
Now, everyone who is using your class needs to inject fs into the constructor. Since this is boring and makes your code more complicated once you have long dependency graphs, developers invented DI containers where they can just configure stuff and the DI container figures out the instantiation.
However, what about just writing pure functions?
function fileTypeCounter(allFiles) {
// count file types
return fileTypes;
}
function getAllFilesInDir(dirname, callback) {
// recursively walk all folders and collect all files
// ...
callback(null, allFiles);
}
// now let's compose both functions
function getAllFileTypesInDir(dirname, callback) {
getAllFilesInDir(dirname, (err, allFiles) => {
callback(err, !err && fileTypeCounter(allFiles));
});
}
Now you have two super-versatile functions out-of-the-box, one which is doing IO and the other one which processes the data. fileTypeCounter is a pure function and super-easy to test. getAllFilesInDir is impure but a such a common task, you'll often find it already on npm where other people have written integration tests for it. getAllFileTypesInDir just composes your functions with a little bit of control flow. This is a typical case for an integration test where you want to make sure that your whole application is working correctly.
By separating your code between IO and data processing, you won't find the need to inject anything at all. And if you don't need to inject anything, that's a good sign. Pure functions are the easiest thing to test and are still the easiest way to share code between projects.
In the past, DI containers as we know them from Java and .NET did not exist. With Node 6 came ES6 Proxies which opened up the possibility of such containers - Awilix for example.
So let's rewrite your code to modern ES6.
class Team {
constructor ({ User }) {
this.User = user
}
getTeam (teamId) {
return this.User.find({ teamId: teamId })
}
}
And the test:
import Team from './Team'
describe('Team', function() {
it('#getTeam', async function () {
const users = [{id: 1, id: 2}]
const fakeUser = {
find: function() {
return Promise.resolve(users)
}
}
const team = new Team({
User: fakeUser
})
const team = await team.getTeam()
expect(team).to.eql(users)
})
})
Now, using Awilix, let's write our composition root:
import { createContainer, asClass } from 'awilix'
import Team from './Team'
import User from './User'
const container = createContainer()
.register({
Team: asClass(Team),
User: asClass(User)
})
// Grab an instance of Team
const team = container.resolve('Team')
// Alternatively...
const team = container.cradle.Team
// Use it
team.getTeam(123) // calls User.find()
That's as simple as it gets; Awilix can handle object lifetimes as well, just like the .NET / Java containers did. This lets you do cool stuff like inject the current user to your services, intantiating your services once per http request, etc.