const Data = require('./Models/Data');
...
let example = new Data( sample );
example.save( function ( err ){
console.log('test);
if ( err ) {
console.log('Error saving Data. 'Error: ', err);
}
});
Any ideas why save() callback function never runs? I mean, the "test" text doesn't show up, while "example" is created just like it should (I mean, when I print it, it looks ok).
Any ideas? TIA
Mongoose async save() function works with a function, which means you don't need to pass it a callback function, but rather use then/catch pattern:
const Data = require('./Models/Data');
...
let example = new Data( sample );
example.save()
.then(() => {
console.log('test);
})
.catch((err) => {
console.log('Error saving Data. 'Error: ', err);
});
see more here
As Nir Levy has already said: the then call can be an alternate way of saving the document.
You can also try supplying the second argument in the save's callback as:
example.save((err, doc) => {
console.log('test');
if (err) {
console.log('Error while saving data: ', err);
} else {
console.log('document is: ', doc);
}
});
Also note that you missed a closing quotemark on the console.log() inside example.save callback
Can you also make sure that your mongodb server is running and you are connected to the mongodb server? if that maybe causing the problem?
This typically occurs when you haven't connected to the database. You can create the model object but none of the Mongoose functions that operate on the database works. They fail silently instead. Make sure you connect with the connect function. You can also listen to the connection and error events to see that you actually get connected:
const dburl = `mongodb://localhost/testdb`;
mongoose.connect(dburl, { useMongoClient: true });
mongoose.connection.on('connected', () => { console.log(`Mongoose connected to ${dburl}`); });
mongoose.connection.on('error', (err) => { console.log(`Mongoose connection error: ${err}`); });
mongoose.connection.on('disconnected', () => { console.log('Mongoose disconnected'); });
Pay attention to the terminal when you start the app and look for Mongoose connected. Finally, you can turn on debug mode in Mongoose to see what's actually going on behind the scenes:
mongoose.set('debug', true);
You can add it right below the connect call, for example.
You don't have to use the promise method by the way. You can use the callback version that you're already using. Just make sure to correct the error with the missing ' in the console.log.
Related
In nodejs Rest api call, puppeteer page evaluate not returning the response and throws the error.
How to return object after executing all the steps, without async
app.get("/api/register", function (req, res) {
res = page.evaluate((res) => {
webex.meetings
.register()
.then(() => {
console.log("Authentication#register() :: successfully registered");
return res.status(200).json({ message: "Successfully Registered" });
})
.catch((error) => {
console.error( "Authentication#register() :: error registering", error);
return res.status(400).json({ message: "Successfully Registered" });
})
}, res);
});
error
:\Users\sansubbu\git\webRTC\node_modules\puppeteer-core\lib\cjs\puppeteer\common\Connection.js:115
const stringifiedMessage = JSON.stringify(Object.assign({}, message, { id }));
^
TypeError: Converting circular structure to JSON
--> starting at object with constructor 'Socket'
| property 'parser' -> object with constructor 'HTTPParser'
--- property 'socket' closes the circle Recursive objects are not allowed.
at JSON.stringify ()
at Connection._rawSend (C:\Users\sansubbu\git\webRTC\node_modules\puppeteer-core\lib\cjs\puppeteer\common\Connection.js:115:41)
at CDPSessionImpl.send (C:\Users\sansubbu\git\webRTC\node_modules\puppeteer-core\lib\cjs\puppeteer\common\Connection.js:320:82)
at ExecutionContext._ExecutionContext_evaluate (C:\Users\sansubbu\git\webRTC\node_modules\puppeteer-core\lib\cjs\puppeteer\common\ExecutionContext.js:211:46)
res is a complex, circular structure that only works in the Node environment. Even if you could, passing it to the browser console via page.evaluate() would take it out of Node, where it belongs, leaving it in an environment where it doesn't make any sense (browsers can't respond to requests as if they were a server).
Instead, try returning a boolean and branching on that on the Node side, where req/res are in their natural environment:
app.get("/api/register", async (req, res) => {
const success = await page.evaluate(async () => {
try {
await webex.meetings.register();
return true;
}
catch (err) {
return false;
}
});
if (success) {
console.log("Authentication#register() :: successfully registered");
return res.status(200).json({message: "Successfully Registered"});
}
console.error("Authentication#register() :: error registering", error);
// probably not the message you want but left as-is...
return res.status(400).json({message: "Successfully Registered"});
});
This is untested since you haven't provided a complete, reproducible example.
page.exposeFunction is another possible tool for triggering Node code based on a condition in the browser, but that seems like overkill here.
Finally, I'm not sure what page is, but typically you need a different page for each request. See this answer for suggested Express + Puppeteer boilerplate.
I'm trying to create a webpage where there is an instance of all the current Projects I am working on, on the left, so I'd need a .forEach() function in order to loop through all of them in order to display it, but on the other side, I need to display the information that is currently selected.
Please first take a look at my code block so I can try to explain the thought process behind what I was trying to do.
So I didn't have any problems selecting the information of the single project that I needed to display on this webpage. I used the .findOne() function in order to pick out the information that I needed.
The problem that I'm facing is that I also need to pass a var that's connected to the .find() function in order to pass through all of the elements of the database. The way I went about this is that I thought I would be able to set the definition of allProjects by manually running the .find() function, and then returning it, thus assigning Projects.find() to allProjects.
app.get('/projects/:url', (req, res) => {
Projects.findOne({ Url: req.params.url }, (err, foundProject) => {
if (err) {
console.log(err);
} else {
res.render('show', {
foundProject: foundProject,
allProjects: Projects.find({}, (err, allProjects) => {
if (err) {
res.send('error');
} else {
return allProjects;
}
})
});
}
});
});
I thought that by returning allProjects and then also having that assigned to allProjects, i'd be able to use the allProjects variable in my show.ejs page.
Unfortunately, I'm getting an error 'allProjects.forEach() is undefined' which leads me to believe that in the app.js where I am defining allProjects, it's not being assigned the correct value that I want it assigned.
It looks like you're expecting return allProjects to do something, but that's actually ignored. Unless you have a callback function you can call, that will go into the void and never be seen by anyone. This is true of virtually all callback functions. They do not care what value that function returns because it's never relevant, what they want is the future value which comes through the callback given to this function.
In other words it plays out like this:
asyncFunctionTakingCallback(function(cb) {
cb(null, value); // This is the important value!
return value; // Nobody cares about this value. Don't even bother.
});
To fix that you need to move the render call inside of the inner-most callback function:
app.get('/projects/:url', (req, res) => {
Projects.findOne({ Url: req.params.url }, (err, foundProject) => {
if (err) {
console.log(err);
// Return here to avoid another level of indentation below
return;
}
Projects.find({}, (err, allProjects) => {
if (err) {
res.send('error');
} else {
res.render('show', {
foundProject: foundProject,
allProjects:
});
}
});
});
});
Now that's still a dizzying amount of code and the nesting here is getting completely out of control even though this is relatively simple Node code.
For comparison here's a version that uses async functions:
app.get('/projects/:url', async (req, res) => {
let foundProject = await Projects.findOne({ Url: req.params.url });
res.render('show', {
foundProject: foundProject,
allProjects: await Projects.find({})
});
});
There's really not much to it this way. What await does is basically stall out on that line and wait for the promise to get resolved or produce an error. Any errors produced should be captured with try { ... } catch as usual.
I'm working with MongoDB in NodeJS,
const { MongoClient, ObjectId } = require("mongodb");
const MONGO_URI = `mongodb://xxx:xxx#xxx/?authSource=xxx`; // prettier-ignore
class MongoLib {
constructor() {
this.client = new MongoClient(MONGO_URI, {
useNewUrlParser: true,
});
this.dbName = DB_NAME;
}
connect() {
return new Promise((resolve, reject) => {
this.client.connect(error => {
if (error) {
reject(error);
}
resolve(this.client.db(this.dbName));
});
});
}
async getUser(collection, username) {
return this.connect().then(db => {
return db
.collection(collection)
.find({ username })
.toArray();
});
}
}
let c = new MongoLib();
c.getUser("users", "pepito").then(result => console.log(result));
c.getUser("users", "pepito").then(result => console.log(result));
and when the last c.getUser statement is executed (that's to say, when I make a SECOND connectio) Mongodb outputs this warning:
the options [servers] is not supported
the options [caseTranslate] is not supported
the options [username] is not supported
the server/replset/mongos/db options are deprecated, all their options are supported at the top level of the options object [poolSize,ssl,sslValidate,sslCA,sslCert,sslKey,sslPass,sslCRL,autoReconnect,noDelay,keepAlive,keepAliveInitialDelay,connectTimeoutMS,family,socketTimeoutMS,reconnectTries,reconnectInterval,ha,haInterval,replicaSet,secondaryAcceptableLatencyMS,acceptableLatencyMS,connectWithNoPrimary,authSource,w,wtimeout,j,forceServerObjectId,serializeFunctions,ignoreUndefined,raw,bufferMaxEntries,readPreference,pkFactory,promiseLibrary,readConcern,maxStalenessSeconds,loggerLevel,logger,promoteValues,promoteBuffers,promoteLongs,domainsEnabled,checkServerIdentity,validateOptions,appname,auth,user,password,authMechanism,compression,fsync,readPreferenceTags,numberOfRetries,auto_reconnect,minSize,monitorCommands,retryWrites,useNewUrlParser]
But I'm not using any deprecated options. Any ideas?
EDIT
After a little discussion with molank in the comments, it looks like open several connections from the same server is not a good practice, so maybe that's what the warning is trying to say (badly I think). So if you have the same problem, save the connection instead of the mongo client.
Reposting from https://jira.mongodb.org/browse/NODE-1868:
The deprecation messages are likely because client.connect is being called multiple times. Overall, calling client.connect multiple times currently (as of driver v3.1.13) has undefined behavior, and it is not recommended. It is important to note that once the promise returned from connect resolves, the client remains connected until you call client.close:
const client = new MongoClient(...);
client.connect().then(() => {
// client is now connected.
return client.db('foo').collection('bar').insertOne({
}).then(() => {
// client is still connected.
return client.close();
}).then(() => {
// client is no longer connected. attempting to use it will result in undefined behavior.
});
The client by default maintains multiple connections to each server it is connected to, and can be used for multiple simultaneous operations*. You should be fine running client.connect once, and then running your operations on the client object
* Note that the client is NOT thread-safe or fork-safe, so it cannot be shared across forks, and it not compatible with node's cluster or worker_threads modules.
The function .connect() takes 3 arguments and is defined as such MongoClient.connect(url[, options], callback). So you need to provide an URL first, then the options and only then you give it the callback. Here is an example from the docs
MongoClient.connect("mongodb://localhost:27017/integration_tests", { native_parser: true }, function (err, db) {
assert.equal(null, err);
db.collection('mongoclient_test').update({ a: 1 }, { b: 1 }, { upsert: true }, function (err, result) {
assert.equal(null, err);
assert.equal(1, result);
db.close();
});
});
Another way to go, since you already created your MongoClient is to use .open instead. It only takes a callback, but you call it from the mongoClient you created (this.client). You ca use it like this
this.client.open(function(err, mongoclient) {
// Do stuff
});
Note
Make sure you check out the MongoClient docs, you'll find a lot of good examples that may guide you even better.
poolSize is deprecated, use maxPoolSize.
I use Jest to test a function which generates a JSON Web Token. It seems that I can't assert the value since when I assert, the callback hasn't been executed yet.
const issueJWT = function issueJWT(req, res, next) {
jwt.sign(signUser, function (err, token) {
if (err) {
next(err);
return;
}
res.locals.token = token;
next();
});
};
This is my test, I mock the request and response, then assert the result:
test('Should return a JWT with proper value if nothing wrong happened', () => {
issueJWT(request, response, mockNext);
const JWT = response.locals.token;
const tokenPayload = jwt.decode(JWT, { complete: true }).payload;
expect(tokenPayload).toHaveProperty('iat');
expect(tokenPayload).toHaveProperty('exp');
expect(tokenPayload).toHaveProperty('id');
});
The error is:
TypeError: Cannot read property 'payload' of null
How to make it work?
According to my knowledge, I think the callback is at the task queue which
means it will be executed when nothing is in the event loop, right? I wanna find a way to defer my assertion, but don't know how...
Thanks for the tips, I use the done, now the test could pass, but the problem is, whenever there is a problem, the error message doesn't make any sense... Any problem to my solution?
test('Should return a JWT with proper value if nothing wrong happened', (done) => {
const callback = () => {
const JWT = response.locals.token;
const tokenPayload = jwt.decode(JWT, { complete: true }).payload;
expect(tokenPayload).toHaveProperty('iat');
expect(tokenPayload).toHaveProperty('exp');
expect(tokenPayload).toHaveProperty('id');
expect(tokenPayload).toHaveProperty('iss');
done();
};
issueJWT(request, response, callback);
});
The error is now:
Timeout - Async callback was not invoked within timeout specified by jasmine.DEFAULT_TIMEOUT_INTERVAL.
Ok, so with some help from #felixKling getting me to actually read the docs, you need to do something like this:
test('Should return a JWT with proper value if nothing wrong happened', done => {
issueJWT(request, response, (e) => {
const JWT = response.locals.token;
const tokenPayload = jwt.decode(JWT, { complete: true }).payload;
expect(tokenPayload).toHaveProperty('iat');
expect(tokenPayload).toHaveProperty('exp');
expect(tokenPayload).toHaveProperty('id');
done();
});
});
I'm not on my dev box so I can't test this, but basically the idea is that you use the 'done' parameter to the test callback to signal that the test is waiting on async code. The test framework will basically wait for your test to call that callback before exiting.
In this case, your next() call from issueJWT is what we're waiting on firing before checking to see if the various objects were updated. If you were not using next() in your middleware, you'd likely need to mock whatever response method you're calling instead (e.g. response.end()) to do your tests.
New to Mocha unit testing, I have a few Mocha examples that are running fine, but I have been trying for hours to get this one to run and no matter what I do, after() is called way earlier than I feel it should. Here's an example:
var dummyData = require('./dummyData.js')
describe('mochaTest', function() {
after(function(done) {
dummyData.cleanDb(function(){
done();
})
});
it('should hit the db to get dummy data and send', function(done) {
dummyData.createDummyData(function(data1, data2, Lookup) {
Lookup.lookup({
data1: data1,
data2: data2
}, function(err, result) {
done();
});
});
});
})
And then in dummyData.js:
exports.createDummyData = function(cb){
doSomeStuff(function (err, patient) {
// Connect to db, get some data to pass.
var Lookup = require(./Lookup.js);
cb(data1, data2, Lookup);
})
}
exports.cleanDb = function(cb) {
// Clear db connections.
cb();
}
The problem is that right after the test is run, the after() function gets called and the Lookup function can't hit the db, because the db connection has been cleared. Why is after being called so early, it shouldn't be called until the it statement calls done() right?
This is an old question, but I have experienced the same issue and could not find any explanation to solve this. Unfortunately I do not have enough reputation to answer as a comment, so I'll share how I solved my issue here.
In my controller, I had a method outlined as follows:
exports.insert = (request, response) => {
UserModel.createUser(request.body)
.then(() => {
respond.status(201).send({message: 'User created successfully
});
};
I realized the issue here, was that my test method wasn't waiting for a response from my User.insert(), because this function is void -- it does not return a value. So the following test would jump straight to calling done(); since there was no reason to wait for a response from User.insert(). This was causing my after hook to run prematurely and close the connection to my database before my UserModel could populate.
// Code is shortened, but my request and response was formed using node-mocks-http package
it('should return status 201', function (done) {
User.insert(request, response);
assert(response._getStatusCode() === 201);
done();
};
I hate to say how long this took me to realize this. But, I needed to return a promise from my controller, so that my test function would have something to wait on. By changing my controller to this:
exports.insert = async (request, response) => {
await UserModel.createUser(request.body)
.then(() => {
response.status(201).send({message: "User created successfully"});
})
.catch(() => {
response.status(400).send({message: "There is already an account with that email"});
});
return response;
And with my test looking something like this:
it('should return a status code of 201', async function () {
await User.insert(request, response);
assert(response._getStatusCode() === 201);
});
The database populates successfully, and my test will wait for my promise to fulfill, thus updating the response before calling my assert. Then the after hook works when expected.
I hope this helps someone else who stumbles across this old thread.