GraphQL x MongoDB - mongodb

I'm trying to read some data from a mongodb database with graphql and mongoose but everytime I query the db it returns null but no error is thrown.
Here's the code:
// Controller.js
exports.user_read = function(id) {
return new Promise((resolve, reject) => {
Contact.findById(id, function(err, user) {
err ? reject(err) : resolve(user);
}
});
}
// Resolver.js
var contact = require('Controller');
...
// root object passed as rootValue to graphqlHTTP
getUser: ({ id }) => {
contact.user_read(id)
}
...
Any tips and help would be appreciated.
P.S. This also seems to be happening with all my queries which take the same Promise format in the controller.js file.

You need to await contact.user_read(id). Without the await, you are simply sending back a Promise. It's most likely pending when it is returned, therefore the null return.
Including Daniel Rearden's suggestion to get rid of the extra Promise, here's what your code would look like:
// Controller.js
exports.user_read = async id => {
return Contact.findById(id, (err, user) => {
err ? reject(err) : resolve(user);
});
}
// Resolver.js
var contact = require('Controller');
...
// root object passed as rootValue to graphqlHTTP
getUser: ({ id }) => {
return await contact.user_read(id)
}
...

Related

Order of save() and find() in NodeJS with MongoDB

I'm trying to create a new record in my MongoDB ("thisPlayer") and save it to my database, then find all records in my database (including the new one) and render them.
I am having trouble understanding why my save() function actually occurs after my find() function. When this code executes, the find() function does not include my new thisPlayer record. However, after the find() runs, the save occurs -- the record is saved to the database AFTER the find() ran.
Thanks in advance!
const playerNumber = async function countPlayers() {
return new Promise((resolve, reject) => {
Player.count(function(err, numOfDocs) {
err ? reject(err) : resolve(numOfDocs);
console.log('I have '+numOfDocs+' documents in my collection');
});
});
}
async function playerProfile() {
var count = await playerNumber();
console.log("count already in db: "+ count);
if (count===0) {
teamCaptain=1;
} else {teamCaptain=0};
count++;
const thisPlayer = new Player({
playerNum: count,
playerName: Name,
});
thisPlayer.save();
Player.find({}, function(err, playaz){
var playerOne;
if (playaz.length > 0) {
playerOne = playaz[0].playerName;
} else {
playerOne = "";
}
res.renderPjax("leavetakings",
{player1: "1: " + playerOne}
);
});
}
playerProfile();
You need to use await
for example.
await Player.find({})

How to check if value already exists in the data received from api before inserting it into db

I am having hard times trying to write data received from a api to db.
I successfully got data and then have to write it to db. The point is to check whether the quote is already exists in my collection.
The problem I am dealing with is that every value gets inserted in my collection, not regarding if it exists or not.
const { MongoClient } = require('mongodb')
const mongoUrl = 'mongodb://localhost/kanye_quotes'
async function connectToDb() {
const client = new MongoClient(mongoUrl, { useNewUrlParser: true })
await client.connect()
db = client.db()
}
async function addQuote(data) {
await connectToDb()
try {
const collection = db.collection('quotes')
let quotes = [];
quotes = await collection.find({}).toArray()
if (quotes = []) { // I added this piece of code because if not check for [], no values will be inserted
collection.insertOne(data, (err, result) => {
if (err) {
return
}
console.log(result.insertedId);
return
})
}
quotes.forEach(quote => {
if (quote.quote !== data.quote) { // I compare received data with data in collection, it actually works fine(the comparison works as it supposed to)
collection.insertOne(data, (err, result) => {
if (err) {
return
}
console.log(result.insertedId);
})
} else console.log('repeated value found'); // repeated value gets inserted. Why?
})
}
catch (err) {
console.log(err)
}
}
Hi it's probably better to set unique: true indexing on your schema. That way you won't have duplicated values.

How to retrieve currentUser data with Vue.js, AWS Amplify, and MongoDB

I am attempting to build a Vue.js App that synthesizes properties of AWS, MongoDB, and Express. I built an authentication page for the app using aws-amplify and aws-amplify-vue. After logging into the app, metadata containing the username for the logged in AWS user is passed into data object property this.name like so:
async beforeCreate() {
let name = await Auth.currentAuthenticatedUser()
this.name = name.username
}
this.name is then added to MongoDB via Axios:
async addName() {
let uri = 'http://localhost:4000/messages/add';
await this.axios.post(uri, {
name: this.name,
})
this.getMessage()
}
I also have a getName() method that I am using to retrieve that data from MongoDB:
async getData () {
let uri = 'http://localhost:4000/messages';
this.axios.get(uri).then(response => {
this.userData = response.data;
});
},
This method, however, returns data for ALL users. I want to reconfigure this method to ONLY return data for .currentAuthenticatedUser(). In my previous experience with Firebase, I would set up my .getData() method with something like:
let ref = db.collection('users')
let snapshot = await ref.where('user_id', '==', firebase.auth().currentUser.uid).get()
...in order to return currentUser information on the condition that 'user_id' in the collection matches the currently logged-in Firebase user.
To achieve this with MongoDB, I attempted to configure the above method like so:
async getData () {
let uri = 'http://localhost:4000/messages';
let snapshot = await uri.where('name', '==', this.name);
this.axios.get(snapshot).then(response => {
this.userData = response.data;
});
},
My thought here was to try and return current user data by comparing 'name' in the MongoDB collection with the logged-in user stored in this.name...but I understand that this might not work because the .where() method is probably unique to Firebase. Any recommendations on how to configure this .getData() to return ONLY data associated with the currentAuthenticatedUser? Thanks!
EXPRESS ROUTES:
const express = require('express');
const postRoutes = express.Router();
// Require Post model in our routes module
let Post = require('./post.model');
// Defined store route
postRoutes.route('/add').post(function (req, res) {
let post = new Post(req.body);
post.save()
.then(() => {
res.status(200).json({'business': 'business in added successfully'});
})
.catch(() => {
res.status(400).send("unable to save to database");
});
});
// Defined get data(index or listing) route
postRoutes.route('/').get(function (req, res) {
Post.find(function(err, posts){
if(err){
res.json(err);
}
else {
res.json(posts);
}
});
});
module.exports = postRoutes;
It is not possible to apply a where clause to a uri AFAIK. What you should do is adding a where clause to the actual query you are making in your backend and, to do that, send the username you want to filter the query with through a query parameter like this: /messages?name=JohnDoe.
So basically if you are using a Node/Express backend, as you suggested, and using Mongoose as the ODM for MongoDB your request would probably be looking something like this:
const Users = require('../models/users.model');
Users.find({}, function (e, users) {
if (e) {
return res.status(500).json({
'error': e
})
}
res.status(200).json({
'data': users
});
})
What you should do is getting the username query parameter through req.query and add it to the options in the first parameter of the find function.
const Users = require('../models/users.model');
let params = {},
name = req.query.name;
if (name) {
params.name = name
}
Users.find(params, function (e, users) {
if (e) {
return res.status(500).json({
'error': e
})
}
res.status(200).json({
'data': users.slice
});
})
That way if you point to /messages?name=John you will get the users with "John" as their name.
Edit:
If your backend is configured in the following way
postRoutes.route('/').get(function (req, res) {
Post.find(function(err, posts){
if(err){
res.json(err);
}
else {
res.json(posts);
}
});
});
what you should do is get the query parameters from inside the get method
postRoutes.route('/').get(function (req, res) {
let params = {},
name = req.query.name
if (name) {
params.name = name
}
Post.find(params, function(err, posts){
if(err){
res.json(err);
}
else {
res.json(posts);
}
});
});

How to pass id to its associated records with Sequelize

I'm building an app with Express on backend, Postgres for db and Sequelize for ORM.
I have 3 associated models:
Post
Event
Publishing, belongs to Post, belongs to Event
When I publish my Post, I update its state to 2, I need to create an Event and Publishing. Publishing will have the postId and eventId, among other things that I'm passing with a query. I tried the following code, it changes the state of the Post, creates an Event and Publishing, but they are not related to each other.
publishPost(req) {
return new Promise((resolve, reject) => {
async.parallel({
changeState: (callback) => {
Post.findAll({
where: { id: req.query.post_id },
attributes: ['id', 'state']
})
.then((updateState) => {
updateState.forEach((postState) => {
postState.updateAttributes({ state: 2 });
});
})
.then((updatedState) => {
callback(null, updatedState);
});
},
createEvent: (callback) => {
Event.create({
instructions: req.query.instructions,
})
.then((createdEvent) => {
callback(null, createdEvent);
});
},
createPublishing: (callback) => {
Publishing.create({
start_date: req.query.startDate,
end_date: req.query.endDate,
})
.then((createdPublishing) => {
callback(null, createdPublishing);
});
}
}, (error, result) => {
resolve(result);
});
});
}
How can I pass the IDs of the two records to the third model?
There are several problems with your implementation!
First of all, your promise never rejects.
Despite of that, you don't actually need to create a promise or use async for this, neither do you want them to run in parallel: If creating a Publishing record needs information about the Event, then you'd want to create first the event, so that you have its id, THEN pass it in the input for the publishing.
Another important thing, take a look at this piece of code:
.then((updateState) => {
updateState.forEach((postState) => {
postState.updateAttributes({ state: 2 });
});
})
.then((updatedState) => {
callback(null, updatedState);
});
Inside the first then, you are making multiple updates, which are promises. They will be dispatched and you never get their values back. Let me explain:
Think if you have just one update to make. It would be like this:
.then((updateStates) => {
return updateStates[0].updateAttributes({ state: 2 });
})
See, we are returning a promise (in this case the update), so the next then will only be called when the update resolves.
If we do this:
.then((updateStates) => {
updateStates[0].updateAttributes({ state: 2 });
})
It will dispatch the update (which takes time) but because you didn't return it, it will pass through and return nothing. Check this out:
var promise1 = new Promise(function(resolve, reject) {
setTimeout(function(){
resolve('foo')
}, 2);
});
var promise2 = new Promise(function(resolve, reject) {
setTimeout(function(){
resolve('foo2')
}, 2);
});
promise1
.then(function(result){
promise2
})
.then(function(result){
console.log(result) //will print undefined
})
promise1
.then(function(result){
return promise2
})
.then(function(result){
console.log(result) //will print foo2
})
So, you are calling multiple updates without getting their values back; if they fail, you'd never know.
Just one more thing: if something goes wrong along the way, you probably want to rollback all the changes made so far, for that, you should use transactions.
Maybe you should try something like this (not tested):
return Post.sequelize.transaction(function (t) {
Post.findAll({
where: { id: req.query.post_id },
attributes: ['id', 'state']
})
.then((updatedStates) => {
var promises = []
updatedStates.forEach((postState) => {
promises.push(postState.updateAttributes({ state: 2 }, {transaction: t}));
});
return Promise.all(promises);
})
.then((results) => {
return Event.create({
instructions: req.query.instructions,
}, {transaction: t})
})
.then((createdEvent) => {
return Publishing.create({
post_id: req.query.post_id,
event_id: createdEvent.id, //or event_id, depends on your model
start_date: req.query.startDate,
end_date: req.query.endDate,
}, {transaction: t})
});
}).then(function (result) {
// Transaction has been committed
// result is whatever the result of the promise chain returned to the transaction callback
}).catch(function (err) {
// Transaction has been rolled back
// err is whatever rejected the promise chain returned to the transaction callback
});

Waiting for meteor cursor in method

I have a large aggrogate query that required me to pass "allowDiskUse: true" as an option. This would not work with the aggegate as described here:
https://github.com/meteorhacks/meteor-aggregate/issues/11
My meteor method is defined here. When I call the method I need to wait for ondata to complete before anything is returned to the client, but nothing I try allows me to get that data in a safe way up to the front end.
Meteor.methods({
'getSummary': function (dept,startDate,endDate,filterType) {
f = myQuery(startdate,enddate,dayFinalGroup);
f.on("data", Meteor.bindEnvironment(function(row) {
//load an array or something here to return
}));
f.once("end", Meteor.bindEnvironment(function() {
// tidy up, in my case end the stream
}));
//here I'd return the array loaded
},
});
This is my front end.
Meteor.call(
'getSummary',0,Session.get('start_date'),Session.get('end_date'),1,
function(error, result){
if(error){
console.log(error);
} else {
Session.set('sumTotals',result);
}
}
);
Finally Got it. I utilized wrapSync
'getSummary': function (dept,startDate,endDate,filterType) {
console.log(dept);
console.log(startDate);
console.log(endDate);
console.log(filterType);
var startdate = new Date(startDate);
var enddate = new Date(endDate);
var arr = [];
f = myQuery(startdate,enddate,dayFinalGroup);
var fetchCursor = Meteor.wrapAsync(function fetchCursor (cursor, cb) {
cursor.each(function (err, doc) {
if (err) return cb(err);
if (!doc) return cb(null, { done: true }); // no more documents
arr.push(doc);
});
});
var myData = fetchCursor(f);
return arr;