I'm doing some query to retrieve some data from a database, and trying to export said data to be used in my nodejs aplication. But everything I've tried so far, does not work.
r.js
async function site() {
var test = await db
.select("*")
.from("site_credentials")
.then(data => {
return data;
});
return test;
}
module.exports = { user: site().then(data=>{return data})}
but I always get Promise pending. Even when I do the imports:
import users = require("./r")
users.then(data=>{return data})
and still doesnt work. How can I fix this?
Thank you,
For starters, there's no reason to resolve a promise and immediately return the same object resolved in its then block. Just omit the "then" if there is nothing else you need to do.
So this:
async function site() {
var test = await db
.select("*")
.from("site_credentials")
.then(data => {
return data; <--- this isn't necessary. Only adds noise unless there is something else you need to do. It's similar to "catching" and immediately "rethrowing" an error... just pointless
});
return test;
}
Can be this:
async function site() {
var test = await db
.select("*")
.from("site_credentials");
return test;
}
Secondly, I'm not really sure why you're trying to resolve it in the export. Just export the function.
module.exports = site;
Then when you require it elsewhere in your app, call it and resolve it there:
const users = require("./r")
users.then(data=>{
// do something with your data here...
})
Note that in your first example, you are exporting an object, containing a "users" property which is the function. If you do it that way, you would need to invoke it like so:
const users = require("./r")
users.users().then(data=>{
// do something with your data here...
})
You can see that users.users clearly doesn't make sense. So, export properly to avoid that. Export only the function itself, not nested inside some other object.
But, if you look closely, you'll notice another thing I did wrong. I'm exporting a "site" function, yet requiring it in as a "users" function. Naming conventions matter. If this function is called "site" here, you ought to require (or import depending on your module loader...) it in as "site"... thus:
const site = require('./r');
Otherwise you just confuse the crud out of a fellow developer.
Related
I have some async code that makes calls to a mongo database and inserts/fetches items. When I am developing locally, the code below works fine. However, when I make the mongoose instance connect to MongoDb Atlas, issues arise. In particular, it seems that my code does not work properly unless I console.log the promise, which makes no sense to me. For example, with the console.log statement, all my tests pass as expected. Without it, 35 tests fail... This is because the promise I am expecting returns null, when it should return some JSON object from the database. Is my code not blocking properly?
It feels like I'm dealing with Schrodinger's cat... Any help would be appreciated. Thanks in advance.
Below is an example promise/function call. I then pass it into _executeQuery. I have await on relevant functions, so I don't think it's because I'm missing the word await somewhere.
async _inSomeAsyncFunction = () => {
const dbQueryPromise = this._dbModel.findById(_id, modelView).lean();
await this._executeQuery({ dbQueryPromise, isAccessPermitted: true })
}
_executeQuery basically gets the result of the promise if the user has access.
private _executeQuery = async (props: {
isAccessPermitted: boolean;
dbQueryPromise: Promise<any>;
}): Promise<any> => {
const { isAccessPermitted, dbQueryPromise } = props;
if (!isAccessPermitted) {
throw new Error('Access denied.');
}
console.log(dbQueryPromise, 'promise'); // without this line, dbQueryResult would be null...
const dbQueryResult = await dbQueryPromise;
return dbQueryResult;
};
After some more testing, I found out that the first API call works but any calls after that returns null...
EDIT:
this._dbModel is some mongoose schema. For example,
const dbSchema= new Schema({
name: String,
});
const dbModel = mongoose.model('DbSchema', dbSchema);
Try replacing your dbQueryPromise as follows:
const dbQueryPromise = this._dbModel.findById(_id, modelView).lean().exec();
Mongoose queries do not get executed unless you pass a callBack function or use exec()
For anyone else having similar problems, here's how I solved it:
I changed
const dbQueryResult = await dbQueryPromise;
to
const dbQueryResult = await dbQueryPromise.then((doc) => {
return doc;
});
im using redis for caching and sequelize as my orm.
i want to cache every query as key and it's result as value.
let me give you an example of how i'm trying to do it
imagine user request for all blogs that are created by himself, normally we would write something like this
blogs.findAll({where:{author:req.params.id}})
when i want to cache something like this i add an attribute named as model and for this example model would be equal to blog after that i will stringify this object and use it as key. this way i can easily create the key and check whether user response is cached or not, but i don't want to rewrite code for every request for checking redis and deciding to make query to database or not and i have 2 models now so i write this piece of code
for (m in models) {
models[m].myFindAll = function (options = {}) {
return new Promise(async function (resolve, reject) {
try {
const key = Object.assign({}, options);
key.model = m;
key.method = "findAll";
var result = await redis.get(JSON.stringify(key));
if (result) {
resolve(JSON.parse(result));
}
result = await models[m].findAll(options);
redis.set(JSON.stringify(key), JSON.stringify(result));
resolve(result);
} catch (err) {
reject(err);
}
});
};
}
as you can see i have an object that contains every model that i have and it is named models.
firstly i added User and after that i added Blog.
my problem is that when i try to use myFindAll function on User Model it won't work becuse it tries to set key.model with value of variable m which will be Blog in the run time, i solved it when i passed the model name an argument to my function but i don't want it that way and i think there should be a better way be i can't find it, isn't there some way of accessing right model through this object?
another thing that i tried to used libraries like sequelize-redis-cache and ... but i wanted to do it my way and i don't want to use this library
I'm using Mockingoose to mock my mongoose calls when running tests with Jest. I tried this but I get an error
mockingoose.Account.toReturn(
["593cebebebe11c1b06efff0372","593cebebebe11c1b06efff0373"],
"distinct"
);
Error:
ObjectParameterError: Parameter "obj" to Document() must be an object, got 593cebebebe11c1b06efff0372
So then I try passing it an array of document objects but it just returns the documents. How di I get it to return just an array or strings?
Here's the code inside the function I'm testing:
const accountIDs = await Account.find({
userID: "test",
lastLoginAttemptSuccessful: true
}).distinct("_id");
I'm open to other ways of mocking my mongoose calls if someone knows of a better way. Thanks!
You can't.
My bad. I looked into mockingoose implementation and realized, it kind of "supports" distinct by implementing a mock, but it actually returned just the given documents, as for the other operations.
Opened a pull request for this issue and added a test, so you're example should be valid and working.
I think the answer is to not use mockingoose. You can do it pretty easily with jest alone.
You can use jest.spyOn() and then mockImplementation() to mock the first call like find() and update(). Here's an example of findOneAndUpdate() where we're checking to make sure the correct object is passed:
// TESTING:
// await Timeline.findOneAndUpdate(query, obj);
//
const Timeline = require("./models/user.timeline");
...
const TimelineFindOneAndUpdateMock = jest.spyOn(Timeline, "findOneAndUpdate");
const TimelineFindOneAndUpdate = jest.fn((query, obj) => {
expect(obj.sendDateHasPassed).toBeFalsy();
expect(moment(obj.sendDate).format()).toBe(moment("2018-11-05T23:00:00.000Z").format());
});
TimelineFindOneAndUpdateMock.mockImplementation(TimelineFindOneAndUpdate);
If you want to mock a chained function you can have it return an object with the next chained function you want to call. Here's an example of how to mock a chained distinct() call.
// TESTING:
// let accountIDs = await Account.find(query).distinct("_id");
//
// WILL RETURN:
// ["124512341234","124512341234","124512341234"]
//
const Account = require("./models/user.account");
...
const AccountFindMock = jest.spyOn(Account, "find");
const AccountFindDistinctResult = ["124512341234","124512341234","124512341234"];
const AccountFindDistinctResult = jest.fn(() => AccountFindDistinctResult);
const AccountFindResult = {
distinct: AccountFindDistinct
};
const AccountFind = jest.fn(() => AccountFindResult);
AccountFindMock.mockImplementation(AccountFind);
And after your test runs, if you want to check how many times a function is called like how many times distinct() was called you can add this:
expect(AccountFindDistinct).toHaveBeenCalledTimes(0);
I'm new to nodejs and trying to learn the basics by rebuilding an existing i2c sensor system.
Got it all running using a named functions and async.series inside a single file. To keep make reusable i now want to create a class which i then can import. unfortunatly i get some errors i don't understand.
class.js
const async = require('async');
const i2c = require('i2c-bus');
class Sensor {
constructor (channel) {
this.channel = channel;
var self = this;
}
openBus (callback) {
bus = i2c.open(self.channel, (err) => {callback()}); // shorted for stackoverflow
}
closeBus (callback) {
bus.close( (err) => {callback()}); //also shorted for better readability
}
connection (callback) {
/* first variation */
async.series([openBus, closeBus], callback);
connection2 (callback) {
/* second variation */
async.series([this.openBus, this.closeBus], callback);
}
}
module.exports = K30;
when i import the class, i can without any problem create a new sensor 'object' and call the functions directly using:
> var Sensor = require('./class.js');
> var mySensor = new Sensor(1);
> mySensor.openBus(foo);
> mySensor.closeBus(bar);
but if i go an try call the wrapper-functions, i get the following errors:
> mySensor.connection(foo);
ReferenceError: openBus is not defined (at 'connection')
> mySensor.connection2(foo);
ReferenceError: self is not defined (at 'openBus')
i believe those errors occure due to my lack of understanding the correct usage of this and self. sadly i can't find any good ead on that topic. any help is highly appreciated.
UPDATE
the solution provided in the first two anwsers was in fact my first approch before starting to use "self" (after some googling [this-that-trick]).
anyways, here is the output/error i get using "this.channel" instead:
> mySensor.connection2(foo);
TypeError: Cannot read property 'channel' of undefined (at openBus)
This is not saved anywhere var self = this; and therefore is lost when the function (constructor is function) ends.
Just remove the above line in constructor and use everywhere the this instead of self.
Its true that this keyword is little tricky in javascript, but if you follow reasonable approach, you should be fine.
You indeed have issue with this and self
Every member inside the class has to be referred by this. If you declare a variable named var EBZ-Krisemendt = "SO user";, to access it, you need to use it with this, eg: console.log(this.EBZ-Krisemendt);
What you need here is
openBus (callback) {
bus = i2c.open(this.channel, (err) => {callback()});
}
and then mysensor.connection2(foo) will work fine.
while i still don't fully understand the reason behind this i fixed my code by getting rid of that "ES6" class definition.
class.js
const i2c = require('i2c-bus');
const async = require('async');
function Sensor(channel) {
let that = this; // make 'this' available in sub-function scope
this.channel = channel;
function openBus(cb) {
// open the bus-connection
bus = i2c.open(that.channel);
}
function closeBus(cb) {
// close the bus-connection
}
function connection(cb) {
async.series([openBus, closeBus], cb);
}
function getReading(cb) {
async.until(
function() {
// loop condition e.g. max tries to get reading
},
function(cb) {
connection(cb); // calling nested synchronous connection-routine
},
function (err) {
// result handling
}
); // end async.until
} // end getReading
return {
getReading: getReading
} // make only 'getReading' available
}
module.exports = {
Sensor: Sensor
} // make 'Sensor' available
in the 'member'-functions i can now use the 'class'-variables of 'Sensor' by accessing them with 'that' (e.g.: 'that.channel')
Detail:
function openBus(cb){
bus = i2c.open(that.channel);
}
if i'd use this instead of that it would only work while calling openBus directly. in my example it's neccessary to call openBus and closeBus in a synchronous manner (for obvious reasons). since async.series is additionally nested inside async.until (sensor might need several tries to response) the scope of this changes. by using that instead i'm able to ignore the scope.
Comment:
since the solution is kinda generally pointing to using nested async-calls inside custom modules i'll slightly alter the titel of the initial question. i'm still hoping for better solutions and/or explanations, so i won't mark my own anwser as accepted yet.
I'm running Express on Node.js and am wondering how I can effectively pass a single database connection context object between distinct Node modules (think of them sort of like application models).
I'd like to do this to be able to start a database transaction in one model and preserve it across calls to other affected models, for the duration of a single HTTP request.
I've seen people attempt to solve this using per-request database connections exposed as middleware before my route is run (taking from a connection pool, then running another piece of middleware after the routes to return the connection to the pool). That unfortunately means explicitly passing around a context object to all the affected functions, which is inelegant and clunky.
I've also seen people talking about the continuation-local-storage and AsyncWrap modules, but I'm unclear how they can solve my particular problem. I tried working with continuation-local-storage briefly but because I primarily use promises and generators in my code, it wasn't able to pass state back from the run method (it simply returns the context object passed into its callback).
Here's an example of what I'm trying to do:
// player-routes.js
router.post('/items/upgrade', wrap(function *(req, res) {
const result = yield playerItem.upgrade(req.body.itemId);
res.json();
});
// player-item.js
const playerItem = {
upgrade: Promise.coroutine(function *(user, itemId) {
return db.withTransaction(function *(conn) {
yield db.queryAsync('UPDATE player_items SET level = level + 1 WHERE id = ?', [itemId]);
yield player.update(user);
return true;
});
})
};
module.exports = playerItem;
// player.js
const player = {
update(user) {
return db.queryAsync('UPDATE players SET last_updated_at = NOW() WHERE id = ?', [user.id]);
})
};
module.exports = player;
// db.js
db.withTransaction = function(genFn) {
return Promise.using(getTransactionConnection(), conn => {
return conn.beginTransactionAsync().then(() => {
const cr = Promise.coroutine(genFn);
return Promise
.try(() => cr(conn))
.then(res => {
return conn.commitAsync().thenReturn(res);
}, err => {
return conn.rollbackAsync()
.then(() => logger.info('Transaction successfully rolled back'))
.catch(e => logger.error(e))
.throw(err);
});
});
});
};
A couple of notes here:
The wrap function is just a little piece of wrapper middleware that allows me to use generators/yield in my routes.
The db module is also just a small wrapper around the popular mysql module, that has been promisified.
What I'd like to do, probably in db.queryAsync, is check if there's a conn object set on the current context (which I'd set around the return Promise... call in db.withTransaction). If so, use that connection to do all subsequent database calls until the context goes out of scope.
Unfortunately, wrapping the return Promise... call in the CLS namespace code didn't allow me to actually return the promise -- it just returned the context object, which is incorrect in my case. It looks like most usages of CLS rely on not actually returning anything from inside the run callback. I also looked at cls-bluebird, but that didn't seem to do what I need it to do, either.
Any ideas? I feel like I'm close, but it's just not all hooking up exactly how I need it to.