Home > Blockchain >  What is the best practice to pass db and logger to routes in Express
What is the best practice to pass db and logger to routes in Express

Time:10-19

Using Express, to pass single instance references like a logger and/or db to routes, I see three options currently.

First is to attach it to the request object via a middleware:

app.use(function(req,res,next){
req.db = db; //this db comes from app.js context where you define it
next();
});

or in app as:

app.set('db', db) // db was previously set
...
app.get('user/:id', users.getUser);

then inside your route function call to use:

module.export.getUser = (req,res) {
...
req.app.get('db').user.find(request.params.id)

Next way is to call some init method on the controller, ie:

const users = require('user.js);
... //setup logger, setup db, then call:
users.init(logger,db); // init method sets local logger/database inside user.js

Last is a variation on the above which forces init to happen once when the module is required:

// setup db and logger, then later call:
require('user.js')(db, logger) //not great style-wise by not having all requires near the top of doc

and in your user.js, set your export like so:

module.exports = (db, logger) => { // returning public functions

This is what I really hate about vanilla JS, and even a framework like Express. It's part of the reason why frameworks like React are so popular because there are set ways to do things or at least commonly accepted patterns.

Ok, back to the topic. What's the best practice for passing single instance things like a logger and database to all your routes/controllers? One of the above or something better?

Unfortunately if you search for "express best practices for routers", you'll get dozens of variations. Is there some goto reference/bible? Some best practice or best pattern guide?

Thanks for any advise/feedback, Paul

CodePudding user response:

I'm confused as to why you would want to perform dependency injection through the routing framework. I've always thought of those as two completely separate layers, express that deals with http requests and then delegates to the logic underneath. Where that logic can be an IOC container of DI'd modules or whatever you want. In JS you can create classes which require dependencies, and then modules which export implementations of those dependencies. The modules will be single impls of those dependencies, letting you spin up whatever combos you want. Do yeah, I don't think express should know anything about your architecture or dependencies, it should simply map routes to service layer functions.

CodePudding user response:

index.js

const express = require('express');
const app = express();

require('./startup/logging')(app);
require('./startup/routes')(app);
require('./startup/db')();

const config = require('./startup/config');

const server = app.listen(config.app.port, () => console.log(`Listening on port ${config.app.port}...`));
module.exports = server;

startup/logging.js

const morgan = require('morgan');

module.exports = function (app) {
 app.use(morgan('tiny'));
}

startup/db

const mongoose = require('mongoose');
const config = require('./config');

module.exports = function () {
  mongoose.connect(config.db.uri, config.db.parameters)
    .then(() => console.log(`Connected to ${config.db.uri}...`));
}

startup/config.js

require('dotenv').config();

const env = process.env.NODE_ENV || 'dev';

const config = {
    'dev': {
        app: {
            port: parseInt(process.env.DEV_APP_PORT),
        },
        db: {
            uri: process.env.DEV_APP_DB,
            parameters: { useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true }
        },
    },
    'test': {
        app: {
            port: parseInt(process.env.TEST_APP_PORT),
        },
        db: {
            uri: process.env.TEST_APP_DB,
            parameters: { useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true }
        },
    },
    'prod': {
        app: {
            port: parseInt(process.env.PROD_APP_PORT),
        },
        db: {
            uri: process.env.PROD_APP_DB,
            parameters: { useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true }
        },
    }
}

module.exports = config[env];

startup/routes.js

const express = require('express');
const users = require('user.js);

module.exports = function (app) {
  app.use('/api/users', users);
}

routes/users.js

const auth = require('../middleware/auth');
const express = require('express');
const router = express.Router();

router.get('/users', auth, async (req, res) => {
  res.send([{name: 'paul', name: 'john'}]);
});

module.exports = router;

middleware/auth

require('dotenv').config();
const jwt = require('jsonwebtoken');

module.exports = function (req, res, next) {
  const token = req.header('x-auth-token');
  if (!token) return res.status(401).send('Access denied. No token provided.');

  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET);
    req.user = decoded;
    next();
  }
  catch (ex) {
    res.status(400).send('Invalid token.');
  }
}

.dotenv

NODE_ENV = 'dev'

DEV_APP_PORT = 5000
TEST_APP_PORT = 4000
PROD_APP_PORT = 3000

DEV_APP_DB = 'mongodb://localhost:27017/example_dev'
TEST_APP_DB = 'mongodb://localhost:27017/example_test'
PROD_APP_DB = 'mongodb://localhost:27017/example'
JWT_SECRET = 'yourJwtSecretKey'
  • Related