Maramal
Maramal

Reputation: 3466

Express + Mongoose Rest scalable project

I am trying to make an scalable RESTful project using Express and Mongoose libraries.

For now, I developed an example in CodeSandbox where I only have an User model, an User repository, an User controller and an User Route. But if I have to repeat all the CRUD operations for more than one model, I guess I have to avoid writing the code again for each model.

This is my current code:

Current user model

const mongoose = require("mongoose");
const bcrypt = require("mongoose-bcrypt");

const userSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true
  },
  password: {
    type: String,
    bcrypt: true
  }
});

userSchema.plugin(bcrypt);

module.exports = mongoose.model("User", userSchema);

Current user repository

const User = require("../models/User");

class UserRepository {
  constructor(model) {
    this.model = model;
  }

  create(object) {
    return this.model.create(object);
  }

  get() {
    return this.model.find().exec();
  }
}

module.exports = new UserRepository(User);

Current user controller

const UserRepository = require("../repositories/user");

function createUser(req, res) {
  const user = req.body;

  UserRepository.create(user)
    .then(response => res.json(response))
    .catch(err => res.status(500).json(err));
}

function getUsers(req, res) {
  UserRepository.get()
    .then(response => res.json(response))
    .catch(err => res.status(500).json(err));
}

module.exports = { createUser, getUsers };

Current user route

const express = require("express");
const UserController = require("../controllers/users");

const router = express.Router();

router.get("/", UserController.getUsers);
router.post("/", UserController.createUser);

module.exports = router;

I think I need to "create" an agnostic solution for each model, which are the only stuff is changing.


So I have created a Default Repository with the following code:

class DefaultRepository {
  constructor(model) {
    this.model = model;
  }

  create(object) {
    return this.model.create(object);
  }

  get() {
    return this.model.find().exec();
  }
}

module.exports = DefaultRepository;

Then I added the code in my User repository:

class DefaultRepository {
  constructor(model) {
    this.model = model;
  }

  create(object) {
    return this.model.create(object);
  }

  get() {
    return this.model.find().exec();
  }
}

module.exports = DefaultRepository;

Then I created a Default Controller that would add the logic function to the repositories:

class DefaultController {
  constructor(repository) {
    console.log(this);
    this.repository = repository;
  }

  create(req, res) {
    const user = req.body;

    this.repository
      .create(user)
      .then(response => res.json(response))
      .catch(err => res.status(500).json(err));
  }

  get(req, res) {
    this.repository
      .get()
      .then(response => res.json(response))
      .catch(err => res.status(500).json(err));
  }
}

module.exports = DefaultController;

And the User Controller:

const UserRepository = require("../repositories/user");
const DefaultController = require("./default");

class UserController extends DefaultController {
  // additional logic
}

module.exports = new UserController(UserRepository);

And finally I added this to the User route:

const express = require("express");
const UserController = require("../controllers/users");

const router = express.Router();

router.get("/", UserController.get);
router.post("/", UserController.create);

module.exports = router;

But of course, this does not work as expected. In fact, it does not work at all. I get a TypeError: Cannot read property 'repository' of undefined. But maybe I am wrong with my whole perspective and this is not a solution for an scalable application or maybe it shouldn't be scalable for this size, in case it is, what am I doing wrong?

This is the non-working Sandbox.

Upvotes: 0

Views: 267

Answers (1)

Felipe Malara
Felipe Malara

Reputation: 1914

The problem is that you are calling the User.* methods implicitly, which does not bind the 'this' state to the methods, you just have to adapt it or stop calling it implicitly

Look the difference

Before:

router.get("/", UserController.get);
router.post("/", UserController.create); 

After:

router.get("/", UserController.get.bind(UserController)); 
router.post("/", UserController.create.bind(UserController))); 

Or you call it like this

router.get("/", (req, res, next) =>
    UserController.get(req, res)
)

Upvotes: 2

Related Questions