I'm working on a project that uses a node/express API and Mongo for storage. I have a function that tries to retrieve data from the storage using the code in the screenshot below. My understanding of async/await is that at the point of await, code execution will pause and proceed when the promise is resolved.
However, the data returned by the function (in the screenshot) is always null, although, the record is there in the db. [The slug is also passed correctly.]
I am starting to believe I am missing something regarding the concept of async/await.
Could anyone please assist me with this?
Am I doing something wrong here?
The calling function is as follows:
async create(req, res, next) {
debug(chalk.blue(`*** Create RSVP`));
console.log(req.body.event); //event is defined and matches db
const event = await Event.findBySlug(req.body.event);
console.log(event); // logs null here
}
Called function:
async function findBySlug(slug) {
return await Model.findOne({ slug: slug })
.populate('user')
.populate('category')
.exec();
}
I have run your code, findBySlug should be working fine. below is sample code for you.
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const ObjectId = Schema.ObjectId;
mongoose.Promise = global.Promise;
mongoose.connect('mongodb://localhost:27017/database-name', {useNewUrlParser: true});
const UserSchema = new mongoose.Schema({
username: String
})
const CategorySchema = new mongoose.Schema({
name: String
})
const PostSchema = new mongoose.Schema({
content: String,
author: {
type: ObjectId,
ref: 'User'
},
category: {
type: ObjectId,
ref: 'Category'
}
})
const Post = mongoose.model('Post', PostSchema, 'posts');
const User = mongoose.model('User', UserSchema, 'users');
const Category = mongoose.model('Category', CategorySchema, 'categories');
async function findBySlug() {
return await Post.findOne({ content: "content name" })
.populate('author')
.populate('category')
.exec();
}
(async function run() {
const event = await findBySlug();
console.log(event); // logs not null here
}())
updating your findBySlug method like this will be enough.
function findBySlug(slug) {
return Model.findOne({ slug: slug })
.populate('user')
.populate('category')
.exec();
}
Related
I want to check whether the requested article page is actually one of the articles of the user who's been authenticated by passportjs req.isAuthenticated() function.
I'm trying to use $elemMatch to achieve that and it logs the same user that's stored in req.user by passport.authenticate() method but when I log user._id it gives me undefined. Why is that?
const express = require('express');
const http = require('http');
const mongoose = require("mongoose");
const session = require("express-session");
const passport = require('passport');
const passportLocalMongoose = require('passport-local-mongoose');
const app = express();
const server = http.createServer(app);
mongoose.connect("mongodb://localhost:27017/userDB");
const articleSchema = new mongoose.Schema({
name: String
});
const userSchema = new mongoose.Schema({
email: String,
password: String,
articles: [articleSchema]
});
const User = new mongoose.model("User", userSchema);
const Article = new mongoose.model("Article", articleSchema);
app.get('/:articlename/:articleId', (req, res) => {
let articleName = req.params.articlename,
articleId = req.params.articleId;
if (req.isAuthenticated()) {
console.log(req.user); // prints the authenticated user
User.find({ articles: { $elemMatch: { "_id": articleId, "name":articleName } } }, function(err, user){
if(err){
console.log(err);
}else{
console.log("This article belongs to " + user); // outputs the expected user
console.log("This article belongs to user with an id " + user._id); // outputs undefined
}
}
)
} else {
console.log('Not Authenticated!');
}
});
Using .exec() triggers a query and returns a Promise which is thenable, adding a callback in .exec() is not a valid way to wait for the promise to be resolved as per Mongoose documentation it is used for
[callback] «Function» optional params depend on the function being
called
Mongoose async operations, like .save() and queries, return thenables.
This means that you can do things like MyModel.findOne({}).then().catch() and await MyModel.findOne({}).exec() if you're using async/await.
Upon calling .exec() it executes your query and returns a promise that you can await in async function by doing so something like
try{
const res = await MyModel.findOne({}).exec()
}catch(err){
console.log(err)
//err handling here
}
Which would await the promise to be resolved and provide you with the result/handle error.
Using await without .exec() is also a valid approach, more information can be found at: Should You Use exec() With await?
Otherwise you can use the thenable approach, which would execute the query and provide you with the result in .then() .catch().
Example:
Band.findOne({name: "Guns N' Roses"}).then(function(doc) {
// use doc
}).catch(function(err){ console.log(err) });
For better understanding, I would highly recommend checking out Mongoose Promises documentation page
I'm using MVC. How can I use the method findOne({}) at my loginController?
I just want to get data from my collection. Also that collection is already exist with couple things. All I want, get data from it. By the way I'm sorry for my English.
App.js:
const dbURL = process.env.DB_URL;
mongoose.connect(dbURL, { useNewUrlParser: true, useUnifiedTopology: true, useCreateIndex: true })
.then((result) => {console.log('bağlandı'); app.listen(8080);})
.catch((err) => console.log(err))
loginController.js:
const loginControllerPost = async (req, res) => {
db.collection.findOne({req.body.username}) //How to properly use the method?
}
You need to define schemas (tables) to tell mongoose what database structure look like. For example, UserSchema:
const conn = mongoose.createConnection('your connection string');
const UserSchema = new Schema({
name: String,
username: String,
password: String,
}, {
collection: 'users', // define collection name here
});
const UserModel = conn.model('UserModel', UserSchema);
const user = await UserModel.findOne({ username: 'Alice' });
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
});
I'm totally new to mongoDB, just coming from MySQL, so I'm trying to add a new document to a mongo database in Node.js, I have the code working except when I have to include a custom object.
Here's my code:
router.post('/', async (req, res) => {
const book= new Book({
title: req.body.book.title,
year_published: req.body.book.year_published,
author: req.body.author // ==> here is the problem without it works fine (comes the full author via body parameter)
});
try {
const savedBook = await book.save();
res.json({
insertedBook: savedBook
});
} catch (err) {
//console.log("Error:" + err);
res.json({error: err});
}
});
The book and author models (simplified):
// ======= AUTHORS ================ //
var mongoose = require('mongoose');
var mongoosePaginate = require('mongoose-paginate-v2');
const schema = new mongoose.Schema({
name: {
type: String,
required:true
},
place_birth: {
type: String,
required:true},
});
schema.plugin(mongoosePaginate);
const Authors = mongoose.model('Authors',schema);
module.exports = Authors;
// ======= BOOKS ================ //
var mongoose = require('mongoose');
var mongoosePaginate = require('mongoose-paginate-v2');
var ObjectId = mongoose.Schema.Types.ObjectId;
const schema = new mongoose.Schema({
title: {
type: String,
required:true
},
year_published: {
type: String,
required:true},
author: [{
type: ObjectId,
ref: 'Authors',
required:false
}],
});
schema.plugin(mongoosePaginate);
const Books = mongoose.model('Books',schema);
module.exports = Books;
Data posting:
{
"book": {
"title": "Entrada con cuernos",
"year_published": "2020",
},
"author": {
"name": "Marcus",
"place_birth": "Moscow",
}
}
What's the proper way to insert a book document?
Thanks
When creating a new Book, Book.author should be a mongoose document, meaning the Author should exist in the mongoDB already.
You need to first save the Author in the DB, then pass it in Boot.author with it's Author._id property set
P.S.: Use singular words when describing your collections:
const Authors = mongoose.model('Authors',schema);
const Authors = mongoose.model('Author',schema); // recommended
mongoose will take care of the plural naming
The first argument is the singular name of the collection your model is for. Mongoose automatically looks for the plural, lowercased version of your model name
for exemple I have a user model like this
const mongoose = require("mongoose");
const Schema = mongoose.Schema;
username: {
type: String,
required: true,
},
Points: {
type: Number,
default: 0,
},
module.exports = User = mongoose.model("users", UserSchema);
then I want to execute a function automatically when user.points is equal to 10 with express js, is there any solution ?
#Yessine, may you should try something like this. You can add checkForPoints wherever you are updating the Points and proceed with your things,
const { Users } = require('/schema.js');
const checkForPoints = async (username) => {
await Users.findOne({ username }, function (err, data) {
if (err) {
console.log("enter error ------", err)
}
if (data && data.Points === 10) {
// Execute your code
}
});
};
// Users schema(schema.js)
const mongoose = require('mongoose');
mongoose.Promise = global.Promise;
mongoose.connect('your db', { useNewUrlParser: true });
const requestSchema = mongoose.Schema({
_id: mongoose.Types.ObjectId,
username: String,
Points: Number
});
module.exports = mongoose.model('users', requestSchema);
Polling is a technique where we check for fresh data over a given interval by periodically making API requests to a server.enables you to periodically check for the newest values of data and do further requests once it enters the desired state.