Search code examples
node.jsmongodbrestmongooserestify

How do you update one-to-many relationships in Node.js?


I'm working an a Node app that uses mongoose, mongodb, and restify. I'd like to find out as to whether it is good practice to have import code from various models when defining the route function. This should be a fairly straightforward question, I just need guidance and I'll be able to code it myself. Thanks for taking the time to read this.

I know how to define the relationship, but I'm not sure how I should be making the actual relationship. That is, in other words, when a car is created, what is the best way to add a car to it.

Please note that I am trying to keep this api RESTful.

I have two models that I would have to linked based on the following schema, which is stored in db/schema.js:

//schema.js
  var restify = require('restify');
  var mongoose = require('mongoose');
  var Schema = mongoose.Schema;
  var ObjectId = Schema.ObjectId;

// User Schema

  exports.User = new Schema({
    id: ObjectId,
    name: String,
    cars: [{ type: ObjectId, ref: 'Car' }],
  });

// Car Schema

  exports.Car = new Schema({
    id: ObjectId,
    name: String,
    _user: { type: ObjectId, ref: 'User' },
  });

Then I build the models in models/..., where I have a different file for each model. Right now they each only have one line of code but I left them as independent files for when I need to write model methods.

In models/car.js:

  mongoose.model('Car', db_schema.Car);

In models/user.js:

  mongoose.model('User', db_schema.User);

And finally, I set up the route with the post and put requests in routes/cars.js

This isn't all of it, just the necessary to answer my question.

  module.exports = function(app) {

    function putCar(req, res, next) {
      Car.findById(req.params.id, function (err, car) {
        if (!err) {
          car.name = req.params.name;
          car.save(function (err) {
            if (!err) {
             // PLACE A
             // current_user = get current user from session
             // current_user.cars.push(car);
              res.send(car);
              return next();
            } else {
              return next(new restify.InternalError(err));
            }
          });
        } else {
          return next(new restify.MissingParameterError('ObjectId   required.'));
        }
      });
     }

    function postCar(req, res, next) {
      var car = new Car(req.params);
      car.save(function (err, car) {
        if (!err) {
          // PLACE B
          // current_user = get current user from session
          // current_user.cars.push(car);
          res.send(car);
        } else {
          return next(err);
        }
      });
    }

    app.post('api/car', postCar);
    app.put('/api/car', putCar);
  }

Would it be appropriate to have code such as the pseudocode in place A and place B? The issue that comes to my mind is that I need to require the User model in the routes/cars.js file, which makes things less modular. Would it be better do this in models/car.js?


Solution

  • I disagree with the assumption that you need a schema at all. I believe that 95% of the time people are using Mongoose they really don't need it because the benefits of having the schema defined like that just don't outweigh the disadvantages for what people are doing with it. If there is some critical and unusual role that verifying that data objects match a schema has in your application then maybe that is different. But you have to do that on the front end anyway and I just don't think that most systems really need to duplicate that effort in a different way on the back end.

    For the vast majority of cases, databases are relatively small and simple. Your case probably isn't an exception. So let's actually take advantage of the fact that we have a schemaless database and lose the boilerplate.

    It looks like you are planning on writing a ton of boilerplate code for every collection. Don't do that. Instead take a look at some CRUD systems for Node like these: https://github.com/AndrewRademacher/auto-crud Does MongoDB have a native REST interface? and/or you can add on to them for your specific needs.

    I think some part of this general philosophy about putting that business about adding the cars to the user object for that session on the back end is probably related to security. In other words, the assumption is that you can't just take the front-end's word for it and save a user object with whatever cars it has, because someone might be trying to hack the front-end. Again, for the vast majority of cases, I just don't see this as a sensible assumption.

    I think it will be much simpler if you just have some generic ways to do CRUD on the back end and just add the cars to the user's data object on the front end. And you can do fine-grained security on the back end on top of a generic CRUD system if you actually need to (i.e., there is a valid business justification, like something that affects the bottom line).

    Mainly that stuff is a holdover belief system from when almost all databases were SQL-based and had rigid schemas with static languages on the back-end, so you almost had to use a bunch of boiler-platy code or rely on metaprogramming that was tricky with static languages.

    I think that in 95% of cases, now that we have things like JSON and dynamic languages, it is wrong to have a bunch of boilerplate that is just differentiated by entity and sometimes field names.