NodeJs/Express MongoDB One-to-Many related documents

nodejs-express-mongodb-one-to-many-related-models-feature-image

In the tutorial, we will show you how to develop One-to-Many related document with NodeJs/Express, MongoDB using Mongoose.

Related post:
Crud RestAPIs with NodeJS/Express, MongoDB using Mongoose
Mongoose Many-to-Many related models with NodeJS/Express, MongoDB

Goal

Prerequisites

Crud RestAPIs with NodeJS/Express, MongoDB using Mongoose
In the above tutorial, we show how to build CRUD RestAPIs with NodeJS/Express and MongoDB using Mongoose:


/nodejs-restapi-mongodb
	/app
		/config
			mongodb.config.js
		/controllers
			customer.controller.js
		/models
			customer.model.js
		/routes
			customer.routes.js
	/node_modules
	package.json
	server.js

Objective

In the tutorial, we show how to develop One-to-Many related documents with NodeJS/Express, MongoDB. Project structure:


/nodejs-restapi-mongodb
	/app
		/config
			mongodb.config.js
		/controllers
			companies.controller.js
			products.controller.js
		/models
			company.model.js
			product.model.js
		/routes
			companies.routes.js
			products.routes.js
	/node_modules
	package.json
	server.js

One-to-Many related models

For working with related documents, we use the ObjectId schema field.

-> CompanySchema:


const Product = require('../models/product.model.js');
const mongoose = require('mongoose'), Schema = mongoose.Schema;

const CompanySchema = mongoose.Schema({
	name: String,
    street: String,
    phone: String
});

module.exports = mongoose.model('Company', CompanySchema);

-> ProductSchema:


const Company = require('../models/company.model.js');
const mongoose = require('mongoose'), Schema = mongoose.Schema;

const ProductSchema = mongoose.Schema({
    code: String,
    name: String,
	details: String,
	company : { type: Schema.Types.ObjectId, ref: 'Company' }
});

module.exports = mongoose.model('Product', ProductSchema);

We can save the references to the related document by assigning the _id value:


var apple = new Company({ 
	name: 'Apple', 
	street: 'Cupertino, CA 95014', 
	phone: '1-408-996-1010' 
});

apple.save(function (err) {
if(err) return console.error(err.stack)

console.log("Apple company is added")

//Apple now exists, so lets create a Product
var iphone7 = new Product({
  code: "A-123",
  name: "Iphone7",
  details: "Price: 649.00 USD & FREE shipping",
  company: apple._id
});

iphone7.save(function (err) {
  if(err) return console.error(err.stack)
  
  console.log("Iphone 7 is added")
});
});

We use populate() to get the Company information in Product:


Product.findOne({ name: req.params.productName })
.populate('company')
.exec(function (err, product) {
	if (err){
		// handle error here
		...
	}
				
	res.send(product);
});

We didn’t add our products to companies, how to get all products by a particular company?

One way, we create a references array field of products in CompanySchema as below:


const Product = require('../models/product.model.js');
const mongoose = require('mongoose'), Schema = mongoose.Schema;

const CompanySchema = mongoose.Schema({
	name: String,
    street: String,
    phone: String,
	products : [{ type: Schema.Types.ObjectId, ref: 'Product' }]
});

module.exports = mongoose.model('Company', CompanySchema);

BUT What is problem? -> We have two places where the information relating companies and products needs to be maintained.

What is the better solution?

-> We get the _id of our company, then use find() to search for this in the company field across all products.


Product.find({ company : req.params.companyId })
.exec(function (err, products) {
	if (err){
		// handle error here
		...
	}
				
	res.send(products);
});

Practice

Create a NodeJS/Express project

Following below guide:
Crud RestAPIs with NodeJS/Express, MongoDB using Mongoose

See dependencies in ‘package.json’ file:


  "dependencies": {
    "body-parser": "^1.18.2",
    "express": "^4.16.3",
    "mongoose": "^5.0.13",
    "npm": "^5.8.0"
  }

Create Model Schema

CompanySchema:


const Product = require('../models/product.model.js');
const mongoose = require('mongoose'), Schema = mongoose.Schema;

const CompanySchema = mongoose.Schema({
	name: String,
    street: String,
    phone: String,
	products : [{ type: Schema.Types.ObjectId, ref: 'Product' }]
});

module.exports = mongoose.model('Company', CompanySchema);

ProductSchema:


const Company = require('../models/company.model.js');
const mongoose = require('mongoose'), Schema = mongoose.Schema;

const ProductSchema = mongoose.Schema({
    code: String,
    name: String,
	details: String,
	company : { type: Schema.Types.ObjectId, ref: 'Company' }
});

module.exports = mongoose.model('Product', ProductSchema);

Route

Company Routes:


module.exports = function(app) {

	var companies = require('../controllers/companies.controller.js')
	
	app.get('/api/companies/init', companies.init);
	app.get('/api/companies', companies.findAll);
}

Product Routes:


module.exports = function(app) {
    var products = require('../controllers/products.controller.js');
	
	app.get('/api/products', products.findAll);
			
	// Find a single Product by Name
    app.get('/api/products/:productName', products.findByName);
	
	// Find all Products of a Company
    app.get('/api/products/company/:companyId', products.findByCompanyId);
}

Controller

Company Controllers:


const Company = require('../models/company.model.js');
const Product = require('../models/product.model.js');

exports.init = (req, res) => {
  var apple = new Company({ 
	name: 'Apple', 
	street: 'Cupertino, CA 95014', 
	phone: '1-408-996-1010' 
  });

  apple.save(function (err) {
    if(err) return console.error(err.stack)
	
	console.log("Apple company is added")
	
    //Apple now exists, so lets create a Product
    var iphone7 = new Product({
	  code: "A-123",
	  name: "Iphone7",
	  details: "Price: 649.00 USD & FREE shipping",
	  company: apple._id
    });

    iphone7.save(function (err) {
	  if(err) return console.error(err.stack)
	  
	  console.log("Iphone 7 is added")
    });
	
	var iPadPro = new Product({
	  code: "A-456",
	  name: "IPadPro",
	  details: "Price: 417.67 USD & FREE shipping",
	  company: apple._id
	});
	
	iPadPro.save(function(err){
		if(err) return console.error(err.stack)
		
		console.log("IPadPro is added");
	});
	
  });
  
  
  var samsung = new Company({ 
		name: 'Samsung', 
		street: 'Seocho District, Seoul, South Korea', 
		phone: '+82-2-2053-3000'
	});
  
  samsung.save(function(err){
	if(err) return console.error(err.stack)
	
	console.log("Samsung company is added")
	
	// Samsung now exists, so lets create a Product
	var galaxyJ7 = new Product({
	  code: "S-012",
	  name: "GalaxyJ7",
	  details: "Price: 219.00 USD & FREE shipping",
	  company: samsung._id	
	});
	
	galaxyJ7.save(function(err){
		if(err) return console.error(err.stack)
		console.log("GalaxyJ7 is added")
	});
	
	var galaxyTabA = new Product({
	  code: "S-456",
	  name: "GalaxyTabA",
	  details: "Price: 299.99 USD & FREE shipping",
	  company: samsung._id
	});
	
	galaxyTabA.save(function(err){
		if(err) return console.error(err.stack)
		console.log("GalaxyTabA is added")
	})
  });
  
  res.send("Done Initial Data!");
}

exports.findAll = (req, res) => {
	Company.find()
    .then(products => {
        res.send(products);
    }).catch(err => {
        res.status(500).send({
            message: err.message
        });
    });
};

Product Controllers:


const Company = require('../models/company.model.js');
const Product = require('../models/product.model.js');

exports.findAll = (req, res) => {
	
	Product.find()
    .then(products => {
        res.send(products);
    }).catch(err => {
        res.status(500).send({
            message: err.message
        });
    });
};

// Find a Products by Name
exports.findByName = (req, res) => {
	Product.findOne({ name: req.params.productName })
	.populate('company')
	.exec(function (err, product) {
		if (err){
			if(err.kind === 'ObjectId') {
				return res.status(404).send({
					message: "Products not found with given name " + req.params.productName
				});                
			}
			return res.status(500).send({
				message: "Error retrieving Products with given Company Id " + req.params.productName
			});
		}
					
		res.send(product);
	});
};

// Find all products by a CompanyId
exports.findByCompanyId = (req, res) => {
	Product.find({ company : req.params.companyId })
	.exec(function (err, products) {
		if (err){
			if(err.kind === 'ObjectId') {
				return res.status(404).send({
					message: "Products not found with given Company Id " + req.params.companyId
				});                
			}
			return res.status(500).send({
				message: "Error retrieving Products with given Company Id " + req.params.companyId
			});
		}
					
		res.send(products);
	});
};

Run & Check results

Run MongDB server by commandline:


\MongoDB\Server\3.6\bin>mongod.exe
2018-04-11T03:11:42.209+0700 I CONTROL  [initandlisten] MongoDB starting : pid=2432 port=27017 dbpath=C:\data\db\ 64-bit host=LOI-COMPUTER
2018-04-11T03:11:42.211+0700 I CONTROL  [initandlisten] targetMinOS: Windows 7/Windows Server 2008 R2
2018-04-11T03:11:42.212+0700 I CONTROL  [initandlisten] db version v3.6.3
2018-04-11T03:11:42.212+0700 I CONTROL  [initandlisten] git version: 9586e557d54ef70f9ca4b43c26892cd55257e1a5
2018-04-11T03:11:42.212+0700 I CONTROL  [initandlisten] OpenSSL version: OpenSSL 1.0.1u-fips  22 Sep 2016
2018-04-11T03:11:42.212+0700 I CONTROL  [initandlisten] allocator: tcmalloc

Run NodeJS/Express application:


\nodejs-restapi-mongodb>node server.js
App listening at http://:::8081
Successfully connected to MongoDB.

– Initial data
-> localhost:8081/api/companies/init

nodejs-mongodb-one-to-many-related-document-initial data

– Get all Companies
-> localhost:8081/api/companies

nodejs-mongodb-one-to-many-related-document-find all company

– Get all Products
-> localhost:8081/api/products

nodejs-mongodb-one-to-many-related-document-find all product

– Find Product by Name
-> localhost:8081/api/product/Iphone7

nodejs-mongodb-one-to-many-related-document-find product by name - polulate company

– Find Products by Company Id
-> localhost:8081/api/products/company/5acd590b203d8e1ac01cb184

nodejs-mongodb-one-to-many-related-document-find all products by company id

Sourcecode

NodeJS-MongoDB-One-to-Many



By grokonez | April 11, 2018.

Last updated on May 3, 2021.



Related Posts


3 thoughts on “NodeJs/Express MongoDB One-to-Many related documents”

  1. This is really great and a lot helpful guide while I was starting a project. The code structure really guided me in a certain way.
    You could also give us the best way to design a server side folder structure (a better approach).

Got Something To Say:

Your email address will not be published. Required fields are marked *

*