mongoose hook or express middleware for trigger database? - javascript

I have this schema in mongoose.
I want to delete a author that will deletes all author's stories.
is best way to do that in middleware express or middleware mongoose?
thanks
const authorSchema = new Schema ({
name : String,
stories : [{ type : ObjectId, ref:'Story'}]
})
const storySchema = new Schema({
title : String
})

Since it involves directly interacting with Mongoose and MongoDB, I'd suggest doing it in Mongoose middleware. Here's how you'd remove all the stories from a certain Author object:
let myAuthor = new Author(/* all the props */);
myAuthor.stories = [];

You can use Mongoose 'remove' middleware.
For example:
authorSchema.pre('remove', (next) => {
storySchema.remove({authorSchema_id: this._id}).exec();
next();
});
And documentation.

Related

Find All Post By User ID

I use mongoose to populate "Post Schema" with information about users whic create the post.
postModule.js
const mongoose = require('mongoose');
const postSchema = mongoose.Schema({
title:String,
description:String,
datePost:Date,
images:[String],
like:[String],
dislike:[String],
author:{
type: mongoose.Schema.Types.ObjectId,
ref:'User'
},
});
postSchema.pre(/^find/,function(next){
this.populate({
path:'author',
select:'photo firstName lastName'
});
next();
});
const Post = mongoose.model('Post',postSchema);
module.exports = Post;
This is the controller build in postController.js
//Get All User Post
exports.getUsersPost = catchAsync(async(req,res,nex)=>{
const userPost = await Post.find({author:req.params.userId});
res.status(200).json({
status:"success",
length:userPost.length,
data:{
post:userPost
}
});
});
postRouter.js
router.route('/get-user-posts/:userId').get(authController.protect,postController.getUsersPost)
I don't know what is wrong, I recive in postman this error Cannot GET /api/v1/get-user-posts/5f719092ba22b8373c72196
There is any option to resolve this problem and the result of request it must to by all the posts created by the user.
If your app.js looks like below, then you endpoint should be:
/api/v1/post/get-user-posts/5f719092ba22b8373c72196.
It takes the whole string in the use() function. So /api/v1/user/**Routes you've defined in your Routes files**
const sessionRoutes = require('./routes/sessionsRoutes');
const userRoutes = require('./routes/userRoutes');
const viewRoutes = require('./routes/viewRoutes');
const postRoutes = require('./routes/postRoutes');
app.use('/api/v1/session',sessionRoutes);
app.use('/api/v1/user',userRoutes);
app.use('/api/v1/post',postRoutes);
app.use('/',viewRoutes);
module.exports = app;
Also add and extra slash just to be sure app.use('/api/v1/user/', userRoutes)

Retrieve date time doing http post request

I have a table and a form that retrieves datas and sends them to a database. After submitting the form, a new table rows is created with the datas just submitted.
I'd like to add a field for each rows with the date and time of the submit.
If I click on submit button, I do a http post request.
Is it possible to save the time and date of the submit?
I'm using mongoose for the db and express for the router.
import Router from 'express';
const router = Router();
router.post('/', isAuthorized(), async (req, res) => {
try {
let fruit = new Fruits(req.body);
await fruit.save();
res.json(fruit);
} catch (err) { res.status(503).send(err); }
});
You can set the timestamps of the POST request on the server-side, with your mongoose models.
In your mongoose Fruits Schema, you can add a timestamps field, with the default set to new Date(). So something like this:
const mongoose = require("mongoose");
const FruitSchema = new mongoose.Schema({
... other Fruit properties...
timestamps: { type: Date, default: () => new Date() }
});
Or instead, you can utilize mongoose's built in timestamps, by passing in a second object to your Schema, like this:
const mongoose = require("mongoose");
const FruitSchema = new mongoose.Schema({
... Fruit properties ...
}, {
timestamps: true
});
This will automatically add createdAt and updatedAt to your newly created Fruits documents.
You can get date and time using the Date object.
pass the below code into the code wherever you want to save. (like MongoDB)
let date_ob = new Date();
Read more about Date

Data not shown after connecting to MongoDB using mongoose in node js

I have a database on mongodb named 'abode' and I am trying to make an API where I can use this data to be displayed on a web app. The file structure of the web app is like this.
app.js
const http = require('http'); //Core Module
const fs = require('fs');
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path'); //Core Module
const mongoose = require('mongoose');
const app = express();
const port = 3001;
Bhk = require('./models/bhk');
//Connect to mongoose
mongoose.connect('mongodb://localhost/abode');
const db = mongoose.connection;
app.get('/api/bhk', (req, res)=>{
Bhk.getBHK(function(err, bhk){
if(err){throw err;}
res.json(bhk);
});
});
bhk.js
const mongoose = require('mongoose');
//BHK schema
const bhkSchema = mongoose.Schema({
Name:{
type: String,
required: true
},
create_date:{
type: Date,
default: Date.now
}
});
const bhk = module.exports = mongoose.model('BHK', bhkSchema);
// Get BHK
module.exports.getBHK = function(callback, limit){
bhk.find(callback).limit(limit);
}
When I run the app it shows an empty array on my webpage. Like this
The server starts successfully and there are no errors shown in my console as well. In the collections of my database I have a collection named - BHK. It consists of two records
{ "_id" : ObjectId("5a6d60827fad61a75bbcb5e9"), "Name" : "2BHK" }
{ "_id" : ObjectId("5a6d60b77fad61a75bbcb5ea"), "Name" : "3BHK" }
However no of these records are shown. I tried searching for various solutions of using a mongo db client, however I could not yet get the required result. I do not know where am I going wrong.
After a lot of surfing through the net and trial and error, I realized MongoDB has a convention for naming collections and databases.
The collection that I had named was 'BHK', which according to its guidelines
and conventions was wrong.
The collection name has to be in lower case and plural which in my case I had to rename the collection to:
bhks

User.findOne() is not a function

passport.use('local-signup', new LocalStrategy({
// by default, local strategy uses username and password, we will override with email
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true // allows us to pass back the entire request to the callback
},
(req, email, password, done) => {
// asynchronous
// User.findOne wont fire unless data is sent back
process.nextTick(() => {
// find a user whose email is the same as the forms email
// we are checking to see if the user trying to login already exists
User.findOne({ 'email' : email },function(err, user){
// if there are any errors, return the error
if (err)
return done(err);
// check to see if theres already a user with that email
if (user) {
return done(null, false, {'errorMessages': 'That email is already taken.'});
} else {
// if there is no user with that email
// create the user
let newUser = new User();
// set the user's local credentials
newUser.name = req.body.fullname;
//newUser.email = email;
newUser.password = newUser.generateHash(password);
// save the user
newUser.save((err)=>{
if (err)
return done(err);
return done(null, newUser);
});
}
});
});
}));
The above code is in node js using passport js authentication and the code of local-signup is not working.
In the above code i am getting the error:
User.findOne() is not a function.
My schema is all right... please help
You need to (if you're not already) create instance of your data with a model like
var UserDetails = mongoose.model('userInfo', UserDetail);
Now you should be able to use .findOne here.
And make sure you're defining structure for your date inside a collection like..
var Schema = mongoose.Schema;
var UserDetail = new Schema({
username: String,
password: String
}, {
collection: 'userInfo'
});
Kindly, use the code below
module.exports = User = mongoose.model('user', UserSchema)
User should be the model name and remember to define const UserSchema = new Schema at the top to create a new model in MongoDB and
user should the route where you have the
router.post('/user') (req, res) => { code here }
with this, you are exporting the mongoose schema to the route user, this which enables findOne to be seen as a mongoose function.
maybe you did not export the Model from your User model folder.
eg:
module.exports = User = mongoose.model("users", UserSchema);
Export the user Model from models directory from file named user.js.
module.exports.User = User
Then Load User model from any other
const {User} = require('../models/user.js');
Note : I'm assuming user models file is named user.js
User.findOne is not a function error
In my case:
CORRECT SYNTAX:
const User = require('../../models/User') // rectified
Error occured due to VS code auto-format my import changed to below
const User = require - '../../models/User'
:- Though this import is the culprit, VS code still validated this import !
You can try this:
user.collection.findOne(...)
use
module.exports = User = mongoose.model("users", UserSchema);
instead of
module.exports = User => mongoose.model("users", UserSchema);
In my case the problem was that instead of writing the below code
module.exports = UserModel;
I had wrote
exports.module = UserModel;
It is how you export and import your Model. If you are defining model as const User = mongoose.model("users", UserSchema); then exporting it by export default User;. you should use the following to import that:
import User from '../db/models/user';
instead of
const User = require('../db/models/user');
TRY THIS
make sure you imported your schama as import User from '../db/models/user'; instead of const User = require('../db/models/user'); if you're using es6 syntax
If your error contains also something like Warning: Accessing non-existent property '...' of module exports inside circular dependency and TypeError: User.findOne is not a function, and generaly the model is an empty {}, then the problem may be the connection (import and export) of the both files. The file that exports the used model can most likely also import the destionation file where the model is used.
Example: Let us have 2 files, user - exporting the User model, and the receiver - example.js
user.js
const mongoose = require('mongoose')
const Schema = mongoose.Schema;
const exmple = require("./example"); //notice how the destination file is also imported in the sender model file
//... all your code and schema here
const User = mongoose.model('User', UserSchema);
module.exports = User;
example.js
const User = require("./user"); // circular dependency
//... your code
This mostly happens, as in my case, when having 2 model files, and importing, and exporting each one into another creating the error. I needed the export model for some User model functions.
I also had this situation where:
const User = require('../../mongoose/User');
User.findOne(...);
returned 'findOne is not a function'
But by changing to:
var User = require('../../mongoose/User');
User.findOne(...);
..it worked.
Hope I helped someone !
In my case I defined User like this ↓
const User = '../../models/User'
instead of ↓
const User = require('../../models/User')

Mongoose and multiple database in single node.js project

I'm doing a Node.js project that contains sub projects. One sub project will have one Mongodb database and Mongoose will be use for wrapping and querying db. But the problem is
Mongoose doesn't allow to use multiple databases in single mongoose instance as the models are build on one connection.
To use multiple mongoose instances, Node.js doesn't allow multiple module instances as it has caching system in require(). I know disable module caching in Node.js but I think it is not the good solution as it is only need for mongoose.
I've tried to use createConnection() and openSet() in mongoose, but it was not the solution.
I've tried to deep copy the mongoose instance (http://blog.imaginea.com/deep-copy-in-javascript/) to pass new mongoose instances to the sub project, but it throwing RangeError: Maximum call stack size exceeded.
I want to know is there anyways to use multiple database with mongoose or any workaround for this problem? Because I think mongoose is quite easy and fast. Or any other modules as recommendations?
According to the fine manual, createConnection() can be used to connect to multiple databases.
However, you need to create separate models for each connection/database:
var conn = mongoose.createConnection('mongodb://localhost/testA');
var conn2 = mongoose.createConnection('mongodb://localhost/testB');
// stored in 'testA' database
var ModelA = conn.model('Model', new mongoose.Schema({
title : { type : String, default : 'model in testA database' }
}));
// stored in 'testB' database
var ModelB = conn2.model('Model', new mongoose.Schema({
title : { type : String, default : 'model in testB database' }
}));
I'm pretty sure that you can share the schema between them, but you have to check to make sure.
Pretty late but this might help someone. The current answers assumes you are using the same file for your connections and models.
In real life, there is a high chance that you are splitting your models into different files. You can use something like this in your main file:
mongoose.connect('mongodb://localhost/default');
const db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', () => {
console.log('connected');
});
which is just how it is described in the docs. And then in your model files, do something like the following:
import mongoose, { Schema } from 'mongoose';
const userInfoSchema = new Schema({
createdAt: {
type: Date,
required: true,
default: new Date(),
},
// ...other fields
});
const myDB = mongoose.connection.useDb('myDB');
const UserInfo = myDB.model('userInfo', userInfoSchema);
export default UserInfo;
Where myDB is your database name.
One thing you can do is, you might have subfolders for each projects. So, install mongoose in that subfolders and require() mongoose from own folders in each sub applications. Not from the project root or from global. So one sub project, one mongoose installation and one mongoose instance.
-app_root/
--foo_app/
---db_access.js
---foo_db_connect.js
---node_modules/
----mongoose/
--bar_app/
---db_access.js
---bar_db_connect.js
---node_modules/
----mongoose/
In foo_db_connect.js
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/foo_db');
module.exports = exports = mongoose;
In bar_db_connect.js
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/bar_db');
module.exports = exports = mongoose;
In db_access.js files
var mongoose = require("./foo_db_connect.js"); // bar_db_connect.js for bar app
Now, you can access multiple databases with mongoose.
As an alternative approach, Mongoose does export a constructor for a new instance on the default instance. So something like this is possible.
var Mongoose = require('mongoose').Mongoose;
var instance1 = new Mongoose();
instance1.connect('foo');
var instance2 = new Mongoose();
instance2.connect('bar');
This is very useful when working with separate data sources, and also when you want to have a separate database context for each user or request. You will need to be careful, as it is possible to create a LOT of connections when doing this. Make sure to call disconnect() when instances are not needed, and also to limit the pool size created by each instance.
Mongoose and multiple database in single node.js project
use useDb to solve this issue
example
//product databse
const myDB = mongoose.connection.useDb('product');
module.exports = myDB.model("Snack", snackSchema);
//user databse
const myDB = mongoose.connection.useDb('user');
module.exports = myDB.model("User", userSchema);
A bit optimized(for me atleast) solution. write this to a file db.js and require this to wherever required and call it with a function call and you are good to go.
const MongoClient = require('mongodb').MongoClient;
async function getConnections(url,db){
return new Promise((resolve,reject)=>{
MongoClient.connect(url, { useUnifiedTopology: true },function(err, client) {
if(err) { console.error(err)
resolve(false);
}
else{
resolve(client.db(db));
}
})
});
}
module.exports = async function(){
let dbs = [];
dbs['db1'] = await getConnections('mongodb://localhost:27017/','db1');
dbs['db2'] = await getConnections('mongodb://localhost:27017/','db2');
return dbs;
};
I have been using this method and it works great for me until now.
const mongoose = require('mongoose');
function makeNewConnection(uri) {
const db = mongoose.createConnection(uri, {
useNewUrlParser: true,
useUnifiedTopology: true
});
db.on('error', function (error) {
console.log(`MongoDB :: connection ${this.name} ${JSON.stringify(error)}`);
db.close().catch(() => console.log(`MongoDB :: failed to close connection ${this.name}`));
});
db.on('connected', function () {
mongoose.set('debug', function (col, method, query, doc) {
console.log(`MongoDB :: ${this.conn.name} ${col}.${method}(${JSON.stringify(query)},${JSON.stringify(doc)})`);
});
console.log(`MongoDB :: connected ${this.name}`);
});
db.on('disconnected', function () {
console.log(`MongoDB :: disconnected ${this.name}`);
});
return db;
}
// Use
const db1 = makeNewConnection(MONGO_URI_DB1);
const db2 = makeNewConnection(MONGO_URI_DB2);
module.exports = {
db1,
db2
}

Categories