NodeJS Construct a custom object without requiring the file - node.js

Is there a way in nodejs to define a custom object type in it's own file, then instantiate it anywhere else in the codebase without needing to require in the file?
For example, every time I create a new map somewhere, I don't have to require in the map definition. I want to be able to do
var user = new User();
from anywhere without requiring in the definition.

If you don't want to require a dependency everywhere then you could use Dependency Injection. Dependency Injection is a pattern for passing dependencies or common information from one context to another.
The power of Dependency Injection is decoupling the Dependency itself with resolving and/or initializing that Dependency. In JavaScript, closures can be used to hide configuration/initialization details of a Dependency by locking in their context to the scope that created them. This means that your files implementing the Dependency don't need to be concerned with how to get it initialized (ie. a config) which, probably also means more thing it needs to require.
Below is an example of how a User can be required in one file, injected into a separate file and then be used as if it were required within the file it was injected to. I chose to use an Express Server and Dependency Injection into a Router as the example because a popular reason to use Node.js is to build a Web API. For another example of Dependency Injection, see this blog post from RisingStack Engineering (you'll need to scroll down to the Dependency Injection section).
The example has 3 files
lib/models/User.js
This is the module that is Injected into our UserRouter
/routes/user.js
This is the UserRouter and it returns a function that accepts a models object which, contains all Models the UserRouter needs. Once the router is created it is returned for use within an Express Server
server.js.
The Express Server that is going to be responsible for injecting the User Model into the UserRouter so it can create new users in a cache.
lib/models/User
class User {
constructor(name) {
this.name = name
}
}
module.exports = User
routes/user.js
const Router = require('express').Router
const UserRouter = models => {
// Use Destructuring to get the User model from the models object passed into the Router
// You can use regular ES5 for this as well: var User = models.User
const {User} = models
const router = new Router()
const users = []
// Return all users in cache
router.get('/', (req, res) => res.status(200).json({users}))
router.post('/', (req, res) => {
const {name} = (req.body || {})
if (!name) {
return res.status(400).send('A User must have a name')
}
// Create the new User
let newUser = new User(name)
// add it to the users cache
users.push(newUser)
// return 201 CREATED and send the user as the message body
return res.status(201).json(newUser)
})
// Return the router instance
return router
}
module.exports = UserRouter
server.js
const express = require('express')
const port = process.env.PORT || 1337
// require our UserRouter
const UserRouter = require('./routes/user')
// import the User model
const User = require('./lib/models/User')
// create an object called Models with the User now as a property
const Models = {User}
const server = express()
// Inject the Models directly into the UserRouter
server.use('/users', UserRouter(Models))
server.listen(port, () => console.log(`Listening on ${port}`))

Related

Injecting dependencies in functions with awilix

I have been following component based strucutre for my projects and I have clone the repo for this purpose listed here
Everything is fine and testable except services.
So I decided to use awilix to inject dependencies in services.
This is what I tried.
I created a container.js file in the loaders.
const awilix = require('awilix');
const db = require('../db');
const AuthService = require('../components/auth/auth.service');
const container = awilix.createContainer({
injectionMode: awilix.InjectionMode.PROXY,
});
function setup() {
container.register({
db: awilix.asValue(db),
doGetAllService: awilix.asValue(AuthService.doRegister),
});
}
module.exports = {
container,
setup,
};
I invoked this container in app.js as below.
const express = require('express');
const app = express();
const cors = require('cors');
// load environment config variables
require('dotenv').config();
require('./loaders/container').setup();
...
In the doc it says, the first argument will be injected dependencies. But when I do console.log I still get undefined.
const doRegister = async (opts, { username, password }) => {
console.log(opts);
const user = await User.create({
username,
password,
role_id: 1, // assign role id here
});
return user;
};
For entire folder structure and source conde please go through the repo.
In order to have awilix resolve any dependencies something must consume from or bootstrap the dependency container. One way is container.resolve(...); (not permanent) and another is by binding the cradle to the request pipeline.
For awilix-express one can bind the cradle to requests like scopePerRequest(container):
Add this to your server middleware
app.use(scopePerRequest(container));
In case you are in need of a complete source, you can check this repository:
https://github.com/fdm-monster/fdm-monster/blob/fb0fc8075cfcd840ae7b50519fe6dd1525b03034/server/app-core.js#L63
I recommend you take a look at awilix-express for a complete server setup with controllers as this truly is cleaner than just function based API routes:
https://www.npmjs.com/package/awilix-express

What's the difference between these 2 ways of calling express.router?

I'm new to Node/Express, and while reading docs and doing some tutorials, I've found that the Express Router is initialized in 2 different ways. Is there any meaningful change between them?
Method 1:
const express = require('express');
const router = express.Router();
router.get('/', (req, res) => {
res.json({'Title': 'HELLO!'});
});
module.exports = router;
Method 2:
const { Router } = require('express');
const router = Router ();
router.get('/', (req, res) => {
res.json({'Title': 'HELLO!'});
});
module.exports = router;
Method 1:
It requires the express module and then calling the Router() function.
Method 2:
This functionality is called Object destructuring in Javascript. We're basically splitting that Particular function(Router) from the express object.
Refer this for more information.
Method 1: The code first imports the Express application object, uses it to get a Router object.
Method 2: Here ES6 object destruction is used to import multiple child modules(or single in this instance) from one single module.
Method 2 is when you need to import multiple child modules from a single module with a single line of code. Else you'll have to include the whole object or import each child module separately.

Why is module.exports=router is needed?

In my node.js server conf file, I set app.use('/user', user.js), which maps /user route to a user.js file.
Then I create subrouting in my user.js file to handle my get or post requests.
My question is: what's the responsibility of module.exports=router at the end of this file?
If I remove it, routing stops working, so I don't understand if it is here to tell my server conf file that there are sub paths in user.js?
var express = require('express');
var user = require('../../models/user');
var db = require('../../models/index');
var router = express.Router();
router.get('/addUser',function (req, res, next) {
db.user.create(req.body)
.then(user => res.json({
data: user,
}))
.catch(error => res.json({
error: true,
data: [],
error: error
}));
});
module.exports = router;
When you do
var user = require('../../models/user');
the user object would get whatever is being exported from the module in user.js. So in user.js, the module.exports=router is mapping a router and all logic that's required to map /user (along with the right callbacks etc...)
If you remove it, your require statement can't acquire an exported object from that module, which is why it would fail. Your user object will be nullified effectively.
Check out here for more info: https://www.tutorialsteacher.com/nodejs/nodejs-module-exports
A router cannot listen(PORT) for requests on its own. The router is useful when you have lots of routes. It's useful for separating your app into multiple modules.
const app = express()
app.listen(port)
app is listening for the requests(not the Router) while your user.js is just a separate js file with some codes.
In module.export ,module is a variable that represents the current module and export is an object. Anything you assign to the module.exports will be expose as a module.
copied: Module in Node.js is a simple or complex functionality organized in single or multiple JavaScript files which can be reused throughout the Node.js application.
Once you do module.export = Router Now you have a newly created module. In nodeJs, you need to require a module before use it. const user = require('./user.js') will do that process. Once you require the node module into your app, you need to tell it to execute by app.use('/' , user)
Or you can do something like below too
in your user.js file,
var user = require('../../models/user');
var db = require('../../models/index');
module.export = (app) =>{
app.get('/addUser',function (req, res, next) {
db.user.create(req.body)
.then(user => res.json({
data: user,
}))
.catch(error => res.json({
error: true,
data: [],
error: error
}));
});
}
in your main index.js,
const app = express()
require('./user.js')(app)

How to keep one instance of the database using express

I am using express along with sequelize to create a REST API. I want to keep just one instance of my database and use it in all the routes I may create. My first thought was to create the instance as soon as the app runs. Something like this.
const express = require('express')
const databaseService = require( './services/database')
const config = require( './config')
const userRoute = require( './routes/user')
const app = express()
databaseService.connect(config.db_options).then(
connectionInstance => {
app.use('user', userRoute(connectionInstance))
app.listen(3000)
}
)
I have not seen something like that so I believe it's not a good approach.
Any ideas ?
A strategy that I use extensively in several production applications to great success is to define the database connection logic in a single file similar to the following:
database.js
const dbClient = require('some-db-client')
let db
module.exports.connectDB = async () => {
const client = await dbClient.connect(<DB_URL>)
db = client.db()
}
module.exports.getDB = () => {
return db
}
Obviously, the connection logic would need to be more complex then what I have defined above, but you can get the idea that you would connect to your database using whichever database client library that you choose and save a reference to that single instance of your database. Then, wherever you need to interface with your database, you can call the getDB function. But first, you would need to make a call to your connectDB function from your server application entry file.
server.js
async function run() {
const db = require('./database')
await db.connectDB()
const app = require('express')()
const routes = require('./routes')
app.use('/', routes)
app.listen(3000, () => {
console.log('App server listening to port 3000')
})
}
You would make the call to initialize your database connection in the server application entry file before initializing express. Afterwards, any place in your application that needs access to the database can simple call the getDB() function and use the returned database object as necessary from there. This does not require overly complicated connection open and close logic and saves on the overhead of constantly opening and closing connections whenever you want to access the database by maintaining just a single database connection that can be used anywhere.

Trying to access configured const across express router files

I'm new to node js so this might be a simple question. In my index.js file I have configured an imported module admin, and I wish to use this configuration across my routed files. How do I pass/access this configured constant? I don't want to have to configure it in every single file.
// In index.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');
const express = require("express")
const myApi = require("./api/my")
admin.initializeApp(functions.config().firebase);
const app = express();
app.use("/api/my", myApi.myRouter);
// In ./api/my/index.js
const myRouter = express.Router();
myRouter.get('/account', function(req, res) {
admin.auth().... <- Need to use the previously configured admin here
}
You can use a top level middleware to define admin property inside the req object:
app.use( function (req, res, next) {
req.admin = admin;
next();
});
Now the fireBase instance is available in any route handler via req.admin
Another approach consist of passing the admin const when you requiring the router file:
index.js:
const myApi = require("./api/my")(admin)
Router file:
const myRouter = express.Router();
module.exports = function (admin) {
myRouter.get('/account', function(req, res) {
admin.auth()....
}
return myRouter;
}
Another way is to use app.set() to store a reference to the admin object and app.get() to get that reference:
index.js
const admin = require('firebase-admin');
app.set('admin', admin);
Router file:
myRouter.get('/account', function(req, res) {
const admin = req.app.get('admin');
admin.auth()....
}
You can make it global .
global.admin = require('firebase-admin');
This can be used across routed files .
But I recommend you to import in every file because global declaration clutter up the global namespace and are slower to look up than local variables.

Resources