Mongoose & Express: POST data from subdocument - javascript

I'm new to Express/Mongoose and backend development. I am attempting to use a Mongoose subdocument in my Schema and POST data from a form to an MLab database.
I am successfully POSTing to the database when only using the parent Schema, but when I attempt to also POST data from the subdocument I am getting an undefined error. How do I properly POST data from a subdocument?
Here is my Schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const bookSchema = new Schema({
bookTitle: {
type: String,
required: true
},
author: {
type: String,
required: true
},
genre: {
type: String
}
});
const userSchema = new Schema({
name: String,
username: String,
githubID: String,
profileUrl: String,
avatar: String,
// I've tried this with bookSchema inside array brackets and also
//without brackets, neither works
books: [bookSchema]
});
const User = mongoose.model('user', userSchema);
module.exports = User;
Here is my route where I attempt to POST to the database:
router.post('/', urlencodedParser, (req, res) => {
console.log(req.body);
const newUser = new User({
name: req.body.name,
username: req.body.username,
githubID: req.body.githubID,
profileUrl: req.body.profileUrl,
avatar: req.body.avatar,
books: {
// All of these nested objects in the subdocument are undefined.
//How do I properly access the subdocument objects?
bookTitle: req.body.books.bookTitle,
author: req.body.books.author,
genre: req.body.books.genre
}
});
newUser.save()
.then(data => {
res.json(data)
})
.catch(err => {
res.send("Error posting to DB")
});
});

Figured it out. I wasn't properly accessing the values using dot notation.
books: {
// All of these nested objects in the subdocument are undefined.
//How do I properly access the subdocument objects?
bookTitle: req.body.books.bookTitle,
author: req.body.books.author,
genre: req.body.books.genre
}
No need to access .books inside of the books object. req.body.books.bookTitle should be req.body.bookTitle and so forth. Leaving this post up in case it helps someone else.

Related

Node.JS - Product is not a constructor error

My code is as follows and gets me "message": "Product is not a constructor" using postman.
Router seems to be properly configured but now I don't know what is the issue.
File product constructor is placed at the right direction. When running node server I get no errors in terminal.
product constructor
const mongoose = require('mongoose');
const productSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: String,
price: Number
});
module.export = mongoose.model('Product', productSchema);
Router for POSTS
const mongoose = require('mongoose');
const Product = require('../models/product'); //import schema, product constructor
//POSTs to products
router.post('/', (req,res,next) => {
//ObjectId method to add new ID
const product = new Product({
_id: new mongoose.Types.ObjectId(),
name: req.body.name,
price: req.body.price
});
//mongoose method to save data
product
.save()
.then(result => {
console.log(result);
})
.catch(err => console.log(err));
res.status(201).json({
message: 'sending POSTs to /products',
createdProduct: product
});
});
It should be module.exports (doc), not module.export:
module.exports = mongoose.model('Product', productSchema);
Right now your module essentially exports a default value (an empty object).
As a sidenote, Schema is expected to be used as a constructor. While the function itself is written the way it'll recall itself with proper syntax if used without new:
if (!(this instanceof Schema)) {
return new Schema(obj, options);
}
... you can both avoid this (albeit miniscule) loss of performance and, what's more important, show the actual intent clearly:
const productSchema = new mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: String,
price: Number
});
Probably you should create instance of Schema
const productSchema = /*(this --->)*/ new mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
name: String,
price: Number
});

Reflecting Changes To Mongo DB After New Post Request - Mongoose & Nodejs

I'm sorry, I'm having a hard time even formulating the question properly. Hopefully it's not too confusing.
I'm building a One To Many Relations in my Mongo DB Atlas. I'm using mongoose and Nodejs.
I'm trying to create a One User to Many Entries. For now let's just say it's a one to one, to remove a layer of complexity. One User To One Entry.
All the code in the backend works, but in short the issue I have is that.
Whenever I make a post request to create a new entry, I can include the user ID that the entry belongs to in the request. But whenever I make a post request to create a new user, I can't include an entry ID in the request, because no requests exist yet for that user. When I create a new entry, mongo db doesn't automatically update the document, to add that new entry to the user associated with it. And I don't know what I need to do on my end to get it to dynamically update the users to include new entries that belong to them.
Here are my models/schemas for users and entries, so you can see the association.
USER SCHEMA
const mongoose = require('mongoose');
const userSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
email: {type: String,
required: true,
unique: true,
displayName: String,
password: {type: String, required: true},
entry: {type: mongoose.Schema.Types.ObjectId, ref: 'Entry', required: true}
}, {collection: "users"});
module.exports = mongoose.model("User", userSchema);
ENTRY SCHEMA
const mongoose = require('mongoose');
const entrySchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
title: {type:String},
body: {type:String, required: true},
user: {type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true},
entryImage: {type: String}
}, {collection: 'entries'});
module.exports = mongoose.model('Entry', entrySchema);
Here are my routes for users and entries. You can see how I set up the logic for the associations
USER ROUTES
const express = require('express');
const router = express.Router();
const mongoose = require('mongoose');
const User = require('../models/user');
const bcrypt = require('bcrypt');
const jwt = require('jsonwebtoken');
router.get('/:userId', (req, res, next) => {
const id = req.params.userId;
User.findById(id)
.select("_id email displayName password entries")
.populate('entry')
.exec()
.then(user => {
res.status(200).json({
id: user._id,
email: user.email,
password: user.password,
entry: user.entry
})
})
.catch(err => {
error: err
})
})
router.post('/signup', (req, res, next) => {
User.find({email: req.body.email})
.exec()
.then(user => {
if(user.length >= 1){
return res.status(422).json({
message: "Username already exists!"
});
} else {
bcrypt.hash(req.body.password, 10, (err, hash) => {
if(err){
return res.status(500).json({
error: err
});
} else {
const user = new User({
_id: new mongoose.Types.ObjectId(),
email: req.body.email,
displayName: req.body.displayName,
password: hash
});
user.save()
.then(data => {
res.status(201).json({
message: "Your user information has been saved in our records",
id: data._id,
email: data.email,
displayName: data.displayName
})
})
.catch(err => {
res.status(500).json({
error: err
})
})
}
})
}
})
.catch(err => {
res.status(500).json({error : err})
})
}); //End of signup post request
EXAMPLE OF AN ENTRY POST REQUEST
EXAMPLE OF A USER POST REQUEST
Please let me know of you have any other questions. Thank you so much, in advance!
The problem is in your schema. You specified explicitly about the _id field.
Your current scheme does not allow mongoose to create this id automatically.
Well, there are two options:
Simplest way. Simply remove _id field from your schema. Mongoose will automatically generate this for you in every create request.
If you want to specify this, pass an option to mongoose so that it can auto-generate this for you
const userSchema = mongoose.Schema({
_id: { type: Schema.ObjectId, auto: true },
})

MongoDB Unable to push to array with findOneAndUpdate()

Been working on a project recently where users subscribe to a class... however I am having trouble with pushing the class to the array... here is my code
const userSchema = new mongoose.Schema({
email: String,
password: String,
secret: String,
classes: [String]
});
const userModel = mongoose.model("Class", userSchema)
app.post("/subscribe", function (req, res) {
const newClass = req.body.subClass;
const id = req.user.id
const ObjectId = mongoose.Types.ObjectId;
userModel.findOneAndUpdate(
{ _id: new ObjectId(id) },
{ $addToSet: { letters: [newClass] } },
{ upsert: false }
);
});
I skipped through the program for useful info so please comment if more code is needed... looking to finally finish this up!

How can I create a post, i have two mongoose models ref'ing each other

Basically I wanna create a Post that has its author to the users name, the one who created it. I also want the Post to be pushed into the array of posts, which the user model has, that is ref'ing to "Post".
I have been googling and watching youtube videos but still i do not understand how i would go about to do this, also i read about populate, but i wanna create a new post and have the author to be the users name, also i want the post to be pushed into the array of posts that the user has.
How would I go about doing this ?
This is the post create controller
exports.postCreatePost = (req, res, ) => {
const {
title,
description,
context
} = req.body;
const post = new Post({
title,
description,
context,
author:
})
}
This is the model.js
const mongoose = require("mongoose"),
Schema = mongoose.Schema,
bcrypt = require("bcryptjs");
const postSchema = new Schema({
title: String,
description: String,
context: String,
author: {
type: Schema.Types.ObjectId,
ref: "User"
}
});
const userSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
},
password: {
type: String,
required: true
},
posts: [{
type: Schema.Types.ObjectId,
ref: "Post"
}]
});
userSchema.pre("save", async function save(next) {
const user = this;
if (!user.isModified("password")) return next();
const hashedPassword = await bcrypt.hash(user.password, 10);
user.password = hashedPassword;
next();
});
const Post = mongoose.model("Post", postSchema);
const User = mongoose.model("User", userSchema);
const userId = new mongoose.Types.ObjectId();
Either let client send you username/id and get it from req.body or when you are authenticating user simply pass the reference to the user to the body of request.
For example when client gives you id/username you can do something like this
const post = new Post({
title,
description,
context,
author: req.body.username or req.body.id
})
If you want to push use this
await findOneAndUpdate({_id: req.body.id}, {
"$push":
{
"posts": post._id
}
})

GET request returns empty array instead of array contents

I'm a newbie, but trying to figure out why my GET request returns an empty array even though I know that the Mongo database collection isn't empty. Each word form in the WordForm collection has a "lexicalform" key whose value is a reference to a LexiconEntry object in that collection. When I submit a GET request with a LexiconEntry ObjectId as a parameter, it returns an empty array in stead of the array contents. Here are my files:
The GET route in my controller:
api.get('/wordforms/:id', (req, res) => {
WordForm.find({lexiconentry: req.params.id}, (err, wordforms) => {
if (err) {
res.send(err);
}
res.json(wordforms);
});
});
The LexiconEntry model:
import mongoose from 'mongoose';
import WordForm from './wordform';
let Schema = mongoose.Schema;
let LexiconEntrySchema = new Schema({
lexicalform: String,
pos: String,
gender: String,
genderfull: String,
decl: String,
gloss: [String],
meaning: String,
pparts: [String],
tags: [String],
occurrences: Number,
wordforms: [{type: Schema.Types.ObjectId, ref: 'Form'}]
});
module.exports = mongoose.model('LexiconEntry', LexiconEntrySchema);
The WordForms model:
import mongoose from 'mongoose';
import LexiconEntry from './lexiconentry';
let Schema = mongoose.Schema;
let WordFormSchema = new Schema({
form: String,
gender: String,
case: String,
number: String,
lexicalform: {
type: Schema.Types.ObjectId,
ref: 'LexicalForm',
required: true
}
});
module.exports = mongoose.model('WordForm', WordFormSchema);
Based on your WordForm schema given above, there is no such property lexiconentry exists in WordForm schema as in your query below.
api.get('/wordforms/:id', (req, res) => {
WordForm.find({lexiconentry: req.params.id}, (err, wordforms) => {
if (err) {
res.send(err);
}
res.json(wordforms);
});
});
The property called lexicalform in WordForm schema resembled the one you are trying above. So, you can change your code as below with this.
api.get('/wordforms/:id', (req, res) => {
WordForm.find({lexicalform: req.params.id}, (err, wordforms) => {
if (err) {
res.send(err);
}
res.json(wordforms);
});
});

Categories