DEV Community

Cover image for How Create Relationships with Mongoose and Node.js ( with real example )
MKilmer
MKilmer

Posted on • Updated on

How Create Relationships with Mongoose and Node.js ( with real example )

Before all, not exists JOIN here! Basically is saved the _id from one collection to another collection ( {type: mongoose.Schema.Types.ObjectId,ref:'NameOfSchema'}).Where the data related it select with the populate() method.

The ideia of the example
Will be a system that allows Users to register and that these users can create Posts

required dependencies

    "body-parser": "^1.19.0",
    "cors": "^2.8.5",
    "express": "^4.17.1",
    "mongoose": "^5.7.5",
    "nodemon": "^1.19.4"
Enter fullscreen mode Exit fullscreen mode

Project Structure
Alt Text

├── index.js

const express = require('express');
const cors = require('cors');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const app = express();

// middlewares
app.use(cors());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
mongoose.connect('mongodb://localhost:27017/relationships',{
    useNewUrlParser: true,
    useUnifiedTopology: true 
})

// routes
app.use(require('./app/routes'));

app.listen(3000, () => console.log('server on!'));

Enter fullscreen mode Exit fullscreen mode

├── models
└── user.js

const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
    name :{
        type:String,
        required: '{PATH} is required!'
    },
    bio: {
        type:String
    },
    website:{
        type:String
    },
    posts : [
        {type: mongoose.Schema.Types.ObjectId,ref:'Post'}
    ]
},{
    timestamps: true
})

module.exports = mongoose.model('User',UserSchema);

Enter fullscreen mode Exit fullscreen mode

├── models
└── post.js

const mongoose  = require('mongoose');
const PostSchema = new mongoose.Schema({
    title:{
        type:String,
        required: '{PATH} is required!'
    },
    subtitle :{
        type: String
    },
    user :{
        type:mongoose.Schema.Types.ObjectId,
        ref:'User'
    }
},{
    timestamps:true
})

module.exports = mongoose.model('Post',PostSchema);
Enter fullscreen mode Exit fullscreen mode

└── user.js ( controller )

const User = require('../../models/user');

module.exports = {
    create : async (req, res) =>{
        const { name, bio, website } = req.body;
        const user = await User.create({
            name,
            bio,
            website
        })

        return res.send(user)
    },

    find : async (req, res) => {
        const user = await User.find()
        return res.send(user)
    },
    postsByUser : async (req, res) => {
       const { id } = req.params;
       const user = await User.findById(id).populate('posts');

        res.send(user.posts);
    }
}

Enter fullscreen mode Exit fullscreen mode

notice that const user = await User.findById(id).populate('posts') is equivalent to :

 user.posts.forEach(async element => {
           const post = await Post.findById(element);
              console.log(post);
        });
Enter fullscreen mode Exit fullscreen mode

└── post.js ( controller )

const Post = require('../../models/post');
const User = require('../../models/user');

module.exports = {
    create : async (req, res) => {

        console.log(req.params);
        user = req.params;
        id = user.id;
        const { title, subtitle} = req.body;
        const post = await Post.create({
            title,
            subtitle,
            user:id
        });
        await post.save();

        const userById = await User.findById(id);

        userById.posts.push(post);
        await userById.save();

        return res.send(userById);
    },
    userByPost : async (req,res)=>{
        const { id } = req.params;
        const userByPost = await Post.findById(id).populate('user');
        res.send(userByPost);
    }
}
Enter fullscreen mode Exit fullscreen mode

└── routes.js

const express = require('express');
const router = new express.Router;
const User = require('./controllers/user/user');
const Post = require('./controllers/post/post');
router.get('/',(req,res)=>res.send('ok'));
// user routes
router.post('/user/create',User.create);
router.post('/user/find',User.find);
router.post('/user/find/post/:id', User.postsByUser);
// post routes
router.post('/post/create/:id', Post.create);
router.post('/post/populate/:id',Post.userByPost);

module.exports = router;
Enter fullscreen mode Exit fullscreen mode

configure └── package.json
to use nodemon in your project, set "start" key in "scripts",passing the nodemon command and the application root filename.

"scripts": {
    "start": "nodemon index.js"
  },
Enter fullscreen mode Exit fullscreen mode

after this, run command "npm start" in your terminal and the application will restart the server every time any files of your project are changed, making development more productive.

Final Result

Alt Text

thanks for everything! 😄

Discussion (17)

Collapse
sarneeh profile image
Jakub Sarnowski

My answer would be: don't. If you need relationships, go for a relational database. IMHO NoSQL databases are for totally different purpose: they're for data, where you don't care about the structure, like logs, analytics data etc. In case you need a structure, NoSQL database is a bad idea because the database doesn't give you any certainty about how the data is structured.

Collapse
mkilmer profile image
MKilmer Author

Thanks for your reply.I know about this, but i just only wanted to show that possible create relationships with NoSQL,even though it's not the best solution for building relationships. 🤘 🤘 🤘

Collapse
sarneeh profile image
Jakub Sarnowski

Sure thing! I don't say your article is bad, it helps a lot! Just wanted to point out my concerns 😄

Collapse
samantafluture profile image
Samanta Fluture

Awesome tutorial!

Just adding how I managed to work in a slightly different way.

My 'create' function from my post controller looks like this:

create : async (req, res) => {

        const user = req.params.id;
        const { title, subtitle} = req.body;
        const post = await Post.create({
            title,
            subtitle,
            user
        });
        await post.save();

        const userById = await User.findById(user);

        userById.posts.push(post);
        await userById.save();

        return res.send(userById);
    },
Enter fullscreen mode Exit fullscreen mode
Collapse
sovietspy2 profile image
Barney

Could you please add request examples?

Collapse
mkilmer profile image
MKilmer Author

I added a gif, showing how the routes work. Thanks for the feedback! 😄

Collapse
sovietspy2 profile image
Barney

Thank you very much! :) cool post

Thread Thread
mkilmer profile image
MKilmer Author

🤘 🤘 🤘

Thread Thread
robotfights profile image
Emmanuel

Hi, am having an issue with my mongoose command installation on windows terminal: npm install --save mongoose

each time i try to install i get a node file opened from vnode.exe.

any help would be appreciated

Thread Thread
xvoodooxl profile image
Fermin Solis

Just do npm install mongoose, the save is not needed anymore since versión 5.x.x I think. Some one correct me if im wrong.

Collapse
manojap profile image
MANOJ AP

router.post('/user/find',User.find); this line not working

Collapse
djdole profile image
DJDole • Edited on

While an interesting exercise, anyone looking production solutions, reconsider.
Never reinvent the wheel.
If you need RDBS, choose RDBS.
Don't shoehorn RDBS into a document-oriented DBS.

Collapse
mkilmer profile image
MKilmer Author

Thanks for your reply! Yes, it is for teaching purposes only, when you want to create relationships, I would not recommend using NoSQL. 🤘 🤘 🤘

Collapse
berkslv profile image
berkslv

Thank you sooo much! mongose docs isn't enough for this :(

Collapse
avoidinglime profile image
AvoidingLime

Hello, I know this is a post from 2019 but I am following in 2021 - having an issue with the create Post POST - can you show us a POSTMAN example of what ID you used to POST the post?

Collapse
oluseyeo profile image
oluseyeo

Thank you, this was excellent.

Collapse
vmuthabuku profile image
vincent muthabuku

also why did you use the router.post request on user/find and user/find/post/:id? Just curious, otherwise great article.
O