Mongoose gives validation error even when required fields are present - node.js

I'm using the mongoose ODM for a project. My schema looks like this:
const applicantSchema = new Schema({
regno: {
type: String,
unique: true,
required: true
},
email: {
type: String,
unique: true,
required: true
}
});
const Applicant = mongoose.model('Applicant', applicantSchema);
I created a wrapper function to add a new document which looks like this:
function addApplicant(newApplicant, callback){
mongoose.connect(url);
const db = mongoose.connection;
console.log(newApplicant);
console.log(typeof newApplicant);
const applicant = new Applicant(newApplicant);
applicant.save((err) => {
if(err) return callback(err);
let info = "successfully saved target";
return callback(null, info);
});
}
I call this function within my route that handles the concerned post request.
router.post('/applicant/response', (req, res) => {
//process sent response here and add to DB
//console.log(req.body);
let newApplicant = {
regno: req.body.regno,
email: req.body.email
}
//console.log(newApplicant);
applicant.addApplicant(newApplicant, (err, info) => {
if(err){ console.log(err); res.end(err);}
res.end('complete, resp: ' + info);
});
});
However, mongoose gives me a validation error (path 'regno' is required) even though I am supplying a value for regno. This happens with all the fields marked as required.
If I remove the 'required: true' option the document is saved to the db as expected.
Any help will be appreciated. :)

It turns out that in this case, something was wrong with the way postman was sending data in a POST request. When I tested this later in postman using JSON as the data format (and ensuring that the Content-Type header is set to application/json), the code worked as expected.
To those facing a similar issue, check the headers postman sends with the request, and ensure that they are what you'd expect them to be.

In your express entry file where you expose your endpoints and setup express you should have app.use(express.json()); written above the endpoint.
const express = require("express");
require("./src/db/mongoose");
const User = require("./src/models/user");
const app = express();
const port = process.env.PORT || 3000;
// THIS LINE IS MANDATORY
app.use(express.json());
app.post("/users", async(req, res) => {
const user = new User(req.body);
try {
await user.status(201).save();
res.send(user);
} catch (error) {
res.status(400).send(error);
}
});
app.listen(port, () => {
console.log(`Server is runnung in port ${port}`);
});

Related

Default generated values (Short Url) changes after refreshing mongoose

I'm using shortid package to shorten my URLs.
Currently, user have this kind of url: https://bucard.co.il/digitalCard/5edd4112eb6ba017d8a4595c (the long string is the _id),
and I want to make it like this: https://bucard.co.il/digitalCard/Y2i1_53Vc
So, I added ShortID field, and as in the documantion, I did this in models/VisitCard.js:
const mongoose = require('mongoose');
const shortid = require('shortid');
const VisitCardsSchema = mongoose.Schema({
ShortID: {
type: String,
default: shortid.generate
},
....
});
module.exports = mongoose.model('VisitCards', VisitCardsSchema);
And my get request in routes/VisitCard.js:
// Get a specific visit card
router.get('/:visitCardId', async (req, res) => {
try {
let cardShortId = req.params.visitCardId;
let allVisitCards = await VisitCard.find({}); // That's how I saw that all the values changed after every get request.
let visitCard = await VisitCard.findOne({ ShortID: cardShortId }); // Never found the card by the short id - even after coping the short id from above, after the next try it changes.
if (!visitCard) {
return res.status(404).json({
message: 'Not existing card.'
});
} else {
return res.status(200).json(visitCard);
}
} catch (error) {
console.log(error);
return res.status(404).json({
message: 'Some server issue accured...'
});
}
});
Now, the proplem is where after every refresh of the browser or another get request, all the ShortID's of all cards are changing (generated again). I want instead that the short url will not be refreshes after every restart of the server, and it will be stored in the Database.
How can I do that after each card gets it's shortID (by default) it will directlly be stored in the DB ?
By the way, I could just have that after every submits of visit card to put some random string to be stored with the other paramters, but I already have visit cards of users in my service.
THANK YOU !!!
Tried reproducing your issue:
const mongoose = require("mongoose");
const shortid = require("shortid");
mongoose.connect("mongodb://localhost/test9999", {
useNewUrlParser: true
});
const db = mongoose.connection;
db.on("error", console.error.bind(console, "connection error:"));
db.once("open", async function() {
await mongoose.connection.db.dropDatabase();
// we're connected!
console.log("Connected");
const VisitCardsSchema = mongoose.Schema({
name: String,
ShortID: {
type: String,
default: shortid.generate
}
});
const VisitCard = mongoose.model("VisitCard", VisitCardsSchema);
const v1 = new VisitCard({name: "abc"});
const v2 = new VisitCard({name: "cde"});
await v1.save();
await v2.save();
await VisitCard.find(function(err, vcs) {
if (err) return console.error(err);
console.log(vcs);
});
console.log("VCS second call:");
await VisitCard.find(function(err, vcs) {
if (err) return console.error(err);
console.log(vcs);
});
});
Apparently, it works perfectly fine. I even commented part with dropping db for a moment and values are persisted correctly.
The problem must be somewhere else - you sure you do not drop the db or the collection with each GET request somewhere else in the code?
This one: await mongoose.connection.db.dropDatabase(); drops entire db, mongooseconnection.connection.db.dropCollection drops a collection. Check if you can find such lines somewhere in your code.

How to send a NODE.JS post request from an Angular Project?

I have a NODE.JS api using expressjs that connects to an SQL Server, and I want to use it in an angular project. I make use two files, a route file and a controllers file. My route file is as follows:
module.exports = (app) => {
const UsrContrllr = require('../Controllers/users.controllers');
//1. GET ALL USERS
app.get('/api/users', UsrContrllr.func1);
//2. POST NEW USER
app.post('/api/user/new', UsrContrllr.func2);
};
And my controllers file is given below:
const mssql = require('mssql');
exports.func1 = (req, res) =>
{
// Validate request
console.log(`Fetching RESPONSE`);
// create Request object
var request = new mssql.Request();
// query to the database and get the records
const queryStr = `SELECT * FROM USERS`;
request.query(queryStr, function (err, recordset) {
if (err) console.log(err)
else {
if (recordset.recordset.toString() === '') {
res.send('Oops!!! Required data not found...');
}
else {
// send records as a response
res.send(recordset);
}
};
});
};
exports.func2 = (req, res) =>
{
// Validate request
console.log(`INSERTING RECORD ${req}`);
// create Request object
var request = new mssql.Request();
// query to the database and get the records
const queryStr = `INSERT INTO GDUSERS (USERCODE, PASSWORD, LANGUAGE, USERCLASS, FIRSTNAME, LASTNAME, CONTACTNO) VALUES ('${req.body.usercode}', '${req.body.password}', 'EN', '0', '${req.body.firstname}', '${req.body.lastname}', '${req.body.contactno}');`;
request.query(queryStr, function (err, recordset) {
if (err) console.log(err)
else {
if (recordset.recordset.toString() == '') {
res.send('Oops!!! Required data not found...');
}
else {
// Send records as response
res.send(recordset);
}
};
});
};
The GET request works well, but when I try to run the POST request directly from the angular application, I get an error stating
Cannot GET URL/api/user/new
The angular code in my angular project is:
signup() {
let headers = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: headers });
console.log(this.user); //User details come from a form
this.http.post(“URL", this.user, options)
.subscribe(
(err) => {
if(err) console.log(err);
console.log("Success");
});
}
I’m not sure whether the angular code I’m using, is right or not, and I don’t know where I’m going wrong. How does one exactly send a http POST request from an Angular project?
this i the way i handled my user signup with http.post calls. my approach is slightly different when signing up user because i am using a promise instead of observable (which i normally use for my servicecalls). but i will show you both ways.
createUser(user: User): Promise < string > {
const promise = new Promise < string > ((resolve, reject) => {
const userForPost = this.createUserForPost(user);
this.http.post(environment.backendUrl + '/api/user/signup', userForPost, this.config).toPromise < HttpConfig > ()
.then(createdUser => {
}).catch(error => {
console.log(error);
});
});
return promise;
}
here another example with an observable
createForumPost(forumPost: ForumPost) {
this.http.post < { message: string, forumPostId: string } > (environment.backendUrl + '/api/forumPosts', forumPost).subscribe((responseData) => {
const id = responseData.forumPostId;
forumPost.id = id;
});
}
i defined my URL somewhere else and then just use the environment.backedUrl + 'path' to define my path (the same as the path in your backend controller)
this is one of my first answers here on SO. i am sry if it is a bit messy
i hope i was able to help with my examples :)

Unable post using express.js (mean)

This is my mongoDb connection
var mongoose = require('mongoose');
// Connection URL
var db = 'mongodb://localhost:27017/employeeDetails';
// Use connect method to connect to the Server
mongoose.connect(db, function (error) {
if (error) {
console.log(error);
}
});
var Schema = mongoose.Schema;
var Employee_Schema = new Schema({
EmployeeName: String,
Designation: String,
Project: String,
Skills:String
});
var Employee = mongoose.model('employees', Employee_Schema);
module.exports=Employee;
This is my api code (express.js)
var express=require('express');
var router=express.Router();
var Employee=require('../database/dataFile');
router.get('/',function(req,resp,next){
Employee.find({},function(err,docs){
resp.send(docs);
})
});
router.post('/create',function(req, resp, next){
var employee_collection =Employee.collection(Employee);
employee_collection.insert(req.body, function(err, doc) {
if(err) throw err;
console.log(doc);
res.json(doc);
});
});
module.exports=router;
This is my $http.post
create(employee: Employee) {
return this.http.post('http://localhost:4500/api/create', employee).map((response: Response) => response.json());
}
I always get
Failed to load resource: the server responded with a status of 500
(Internal Server Error)
FYI: $http.get works fine. It fetches data from MongoDb. The following hit the get method in api
getEmployeeList() {
return this.http.get('http://localhost:4500/api');
}
Please note I am taking example from here
This tutorial does not have Post functionality and I am trying to implement.
There Is A problem with the query, If you want to fetch the collection use:
mongoose.getCollection(<collection name>), But here you are calling the collection function on a model object, not the mongoose one.
Instead, I Would recommend using .create directly:
Employee.create(req.body, function(err,result){
if(err){throw Error(err)}
return res.json(result);
})

Not able to display the current data sent from server

i am using nodejs as server, I am sending parameters to mongodb and saving it in database after that i am fetching it from database. But when i try to fetch the data i can't able to see the current data in nodejs terminal ,but it will be present in database. Again if i send the other data i will able to see the previous data but not the current data which i have sent now. I think my server is calling find function before save function. what should i do to make my save function to complete its task and then it should call the find function.
this mongodb code
import mongoose from 'mongoose';
var Schema = mongoose.Schema;
//connect to a MongoDB database
var db = mongoose.connect('mongodb://127.0.0.1:27017/student');
mongoose.connect('connected', function() {
console.log("database connected successfully")
});
var userSchema = new Schema({
Name: {
type: String,
required: true
},
Age: {
type: Number,
required: true
}
}, {
collection: ('studentcollection2')
});
var User = mongoose.model('User', userSchema);
function createStudent(name, age) {
var list = new User({
Name: name,
Age: age
});
list.save(function(err) {
if (err) throw err;
console.log("SUCCESSFUL");
});
}
function listStudent() {
User.find({}, function(err, studentcollection2) {
if (err) throw err;
console.log(studentcollection2);
});
}
exports.createStudent = createStudent; //module.exports = User;
exports.listStudent = listStudent;
this is my server code
import config from './config';
import express from 'express';
const server = express();
import mongodbInterface from './mongodbInterface';
server.set('view engine', 'ejs');
server.get('/', (req, res) => {
res.render('index', {
content: '...'
})
});
console.log(typeof mongodbInterface.createStudent);
mongodbInterface.createStudent("a9", 112);
mongodbInterface.listStudent();
server.use(express.static('public'));
server.listen(config.port, () => {
console.info('express listening on port', config.port);
});
I haven't run your code, but I imagine it's coming down to these two lines:
mongodbInterface.createStudent("a9", 112);
mongodbInterface.listStudent();
Both of these statements call asynchronous functionality. That is to say that when you call createStudent("a9", 112);, that functionality will run asynchronously in the background, and Node will continue to call listStudent(); straight away. Therefore, your createStudent method may not have written the data to the database by the time your listStudent function is run.
To solve this, you need to make use of callback functions to only retrieve the data once it's actually been saved. For example, you could do the following:
function createStudent(name, age, cb) {
var list = new User({
Name: name,
Age: age
});
list.save(function(err) {
if (err) return cb(err);
return cb();
});
}
function listStudent(cb) {
User.find({}, function(err, studentcollection2) {
if (err) return cb(err);
return cb(null, studentCollection2);
});
}
Then, in your server code:
mongodbInterface.createStudent("a9", 112, function(err) {
if (err) throw err;
mongodbInterface.listStudent(function(err, students) {
console.log(students); // You should see the newly saved student here
});
});
I'd recommend reading more about Node.js callbacks here - they are really core to the usage of Node.js.
The mongo methods are async so the data will write whenever it's ready. Use a callback function, promise or async/await to wait for the result without blocking the event loop and control order of execution.

Best way to connect to MongoDB using Node.js [duplicate]

This question already has answers here:
How do I manage MongoDB connections in a Node.js web application?
(13 answers)
Closed 4 years ago.
I've read a few guides on how to use Mongo with Node, and they all seem to connect to databases differently. One particular way that worked well for me was:
MongoClient.connect("mongodb://localhost:27017/exampleDb", function(err, db) {
if(err) { return console.dir(err); }
db.createCollection('users', function(err, collection) {});
//Do all server/database operations in here
});
However, this seems inefficient/odd to me, I would have to reconnect to the database every time there is an app.get(), like for making a new user or retrieving information.
Another way that seems better suited to me is
var mongoose = require("mongoose")
var db = mongoose.connect("localhost:27107/users");
db.createCollection('users', function(err, collection) {});
I've seen several sites do something along these lines, but I personally can't get the above to work. I keep getting the error TypeError: db.createCollection is not a function server-side. So, my question is why the above code doesn't work, if the first code is a good alternative, and if there are any other ways to do this.
You can use a global variable to hold the connection (e.g. db), for example:
var db = null // global variable to hold the connection
MongoClient.connect('mongodb://localhost:27017/', function(err, client) {
if(err) { console.error(err) }
db = client.db('test') // once connected, assign the connection to the global variable
})
app.get('/', function(req, res) {
db.collection('test').find({}).toArray(function(err, docs) {
if(err) { console.error(err) }
res.send(JSON.stringify(docs))
})
})
Or, if you prefer, you can also use the Promise object that is returned by MongoClient if it is called without a callback argument:
var conn = MongoClient.connect('mongodb://localhost:27017/') // returns a Promise
app.get('/', function(req, res) {
conn.then(client=> client.db('test').collection('test').find({}).toArray(function(err, docs) {
if(err) { console.error(err) }
res.send(JSON.stringify(docs))
}))
})
Please note that I used the ES6 fat arrow function definition in the second example.
You are absolutely correct that you should not call MongoClient every time. Using a global variable or Promises allows the MongoDB node.js driver to create a connection pool, which achieves at least two good things:
Connections are reused in a pool, so there is no multiple expensive setup/teardown process for the lifetime of your application. You connect once, and let the driver take care of the rest for you.
You can control the amount of connection your application makes into the database, by limiting the size of the connection pool.
Edit 2018-08-24: The MongoClient.connect() method in node.js driver version 3.0 and newer returns a client object instead of a database object. The examples above were modified to keep it up to date with the latest node.js driver version.
I've written a tutorial on how to reuse mongodb connection in express. You can see here. Basically, it's a simple module, which you can use with expressjs like that:
var connection = require('./dbconnection');
// url and optional config.
app.use(connection(app, 'mongourl', {});
And here's the code for the connection:
module.exports = function(app, uri, opts) {
if (typeof uri !== 'string') {
throw new TypeError('Error: Unexpected mongodb connection url');
}
opts = opts || {};
var property = opts.property || 'db';
var connection;
return function expressMongoDb(req, res, next) {
if (!connection) {
connection = MongoClient.connect(uri, opts);
}
connection
.then(function (db) {
req[property] = db;
app.set('mongodb', db);
next();
})
.catch(function (err) {
connection = undefined;
next(err);
});
};
};
You could use it this way:
that's server.js file:
import path from 'path'
import express from 'express'
import bodyParser from 'body-parser'
import morgan from 'morgan'
import db from './server/database'
import routes from './server/routes'
import webpack from 'webpack'
import webpackDevMiddleware from 'webpack-dev-middleware'
import webpackHotMiddleware from 'webpack-hot-middleware'
import webpackConfig from './config/webpack'
const app = express()
const port = process.env.PORT || process.env.NODE_PORT
const compiler = webpack(webpackConfig)
db(λ => {
app.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath: webpackConfig.output.publicPath }))
app.use(webpackHotMiddleware(compiler))
app.use(morgan('dev'))
app.use(bodyParser.json({ limit: '20mb' }))
app.use(bodyParser.urlencoded({ limit: '20mb', extended: false }))
app.use('/static', express.static('static'));
//app.use('/api', jwt)
app.use('/api', routes())
app.set('json spaces', 2)
app.get('*', function(request, response) {
response.sendFile(path.resolve(__dirname, 'index.html'))
})
app.listen(port, (error) => {
if (error) {
console.error(error)
throw error
} else {
console.info(`==> 🌎 Listening on port ${port}. Open up http://localhost:${port}/ in your browser.`)
}
})
})
server/database.js
import mongoose from 'mongoose'
export default callback => {
const { MONGO_URL, MONGO_PORT, MONGO_DB } = process.env
mongoose.connect(`mongodb://${MONGO_URL}:${MONGO_PORT}/${MONGO_DB}`, error => {
if (error) {
console.error('Please make sure your MongoDB configuration is correct and that service is running')
throw error
}
})
callback()
}
Then you'll have to define your mongoose models, for instance:
import mongoose, { Schema } from 'mongoose'
const ideaSchema = new Schema({
title: {
type: String,
required: true
},
slug: {
type: String,
required: true,
unique: true
},
description: {
type: String,
required: true
}
})
export default mongoose.model('Idea', ideaSchema)
And just use controllers this way:
import HttpStatus from 'http-status-codes'
import mongoose from 'mongoose'
import sanitize from 'sanitize-html'
import slug from 'slug'
import Idea from '../models/idea'
const findAllIdeas = (req, res) => {
Idea.find()
.select('user title slug createdAt updatedAt')
.populate({
path: 'user',
select: 'firstName lastName'
})
.then(data => res.status(HttpStatus.OK).json(data))
.catch(error => res.status(HttpStatus.BAD_REQUEST).json(error))
}
export default { findAllIdeas, findIdeaBySlug, createIdea, addComment }
You will not have to connect to mongoDB for each get request.
So your route will look like that. Quite straightforward:
import { Router } from 'express'
import controller from '../controllers/idea'
const router = Router()
router.route('/')
.get(controller.findAllIdeas)
.post(controller.createIdea)
router.route('/:slug')
.get(controller.findIdeaBySlug)
router.route('/comment')
.post(controller.addComment)
export default router
My go-to code is as follows:
mongoose.connect(YOUR_URL ,function(err) {
if (err) {
console.log(err);
}else{
console.log("Connected to DB");
}
});
Also try connecting to just localhost:27107, that may be your problem.

Resources