From previous answers this probably has something to do with my connection but I can't seem to place how to find the issue. I have a few segment of codes that looks like this and the result is a stall after any model function is called
connection - prints success when the server starts
mongoose.Promise = require('bluebird');
mongoose.connect('mongodb://localhost/test', { useMongoClient: true, promiseLibrary: require('bluebird') })
.then(() => console.log('connection succesful'))
.catch((err) => console.error(err));
model.js
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var holdingsSchema = new Schema({
pair: String,
amount: Number
});
var holdingsModel = mongoose.model('holdingsModel', holdingsSchema);
module.exports = holdingsModel
and api endpoint
holdingsModel.find(function(err, results) {
if (err) {
console.log('there is an error')
}
return res.json(results);
}).then(results => console.log('the results: ' + results));
});
At the end of everything I receive an err: empty response.
find() needs a criteria object. The second argument is the callback. When you are using promises you don't need the callback. Finally, you have to handle the errors with catch().
holdingsModel.find({ pair: 'test' }).exec()
.then(results => res.status(200).json(results))
.catch(err => res.status(400).json(err));
The exec() is used to return a proper promise, as mentioned here.
Related
I created some sample code to demonstrate my issue on a smaller scale. I would like a solution that doesn't involve adding 'unique: true' to my model, if possible, because I seem to run into similar problems in many different scenarios:
const express = require('express')
const mongoose = require('mongoose')
const app = express()
const PORT = 6000
app.use(express.json())
// Initializing mongoose model and connection
const SampleSchema = mongoose.Schema({
username: String,
password: String
})
const Sample = mongoose.model('sample', SampleSchema)
mongoose.connect('mongodb://localhost/testdatabase', {
useNewUrlParser: true,
useUnifiedTopology: true
})
// Running my post request
app.post('/api', async (req, res) => {
await Sample.findOne({
username: req.body.username
}).then(data => {
if(data) {
console.log(data)
return res.json('This user already exists in my database')
}
})
await Sample.create({
username: req.body.username,
password: req.body.password
})
return res.json('User created')
})
app.listen(PORT, () => {
console.log('Server running on 6000')
})
Here is my request and database the first time I send a request:
This is as intended. However, if I send the same request a second time:
I want the code to stop on the first 'res.json' if that line of code is executed - basically, in this example I don't want to create a new Sample if one with the same username already exists. I do realize that in this case I can approach the issue differently to solve the problem, but I want to know why my 'Sample.create' line runs, and if there's a way to prevent it from running aside from the aforementioned method.
This is because the .then callback executes after the enclosing function has already finished. In this code here:
await Sample.findOne({
username: req.body.username
}).then(data => {
if(data) {
console.log(data)
return res.json('This user already exists in my database')
}
})
The function being returned from is the data => ... arrow function passed to .then, not the enclosing request handler, so it doesn't prevent subsequent code from executing.
You want to rewrite that bit to use async/await syntax as well:
const data = await Sample.findOne({
username: req.body.username
})
if(data) {
console.log(data)
return res.json('This user already exists in my database')
}
You might want to read up a bit on async/await and Promises in general-- asynchronous code can be quite confusing at first! https://developers.google.com/web/fundamentals/primers/async-functions
The error message:
WARN Possible Unhandled Promise Rejection (id: 1):
Error: INVALID_STATE_ERR
send#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:31745:26
initialiseWebsocket#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:100544:21
loadUserData$#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:100610:40
tryCatch#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:7739:23
invoke#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:7912:32
tryCatch#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:7739:23
invoke#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:7812:30
http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:7822:21
tryCallOne#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:28596:16
http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:28697:27
_callTimer#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:29113:17
_callImmediatesPass#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:29152:17
callImmediates#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:29370:33
__callImmediates#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:3279:35
http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:3057:34
__guard#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:3262:15
flushedQueue#http://localhost:8081/index.bundle?platform=android&dev=true&minify=false&app=com.dcgymappfrontend&modulesOnly=false&runModule=true:3056:21
flushedQueue#[native code]
invokeCallbackAndReturnFlushedQueue#[native code]
The useEffect that is being accused of being a problem:
React.useEffect(() => {
// Fetch the token from storage then navigate to our appropriate place
const loadUserData = async () => {
let userData;
try {
userData = await retrieveUserData();
} catch (e) {}
if(userData){
dispatch({ type: 'RESTORE_USER_DATA', userData: userData });
getChatData(userData, setChats, dispatch);
if(userData && !websocketInitialised){
console.log('web init called from *load user data*')
setWebsocketInitialised(true)
initialiseWebsocket(userData);
}
}
else{
dispatch({ type: 'RESTORE_USER_DATA_FAILED'});
}
};
loadUserData();
}, []);
The initialliseWebsocket function
function initialiseWebsocket(userData){
console.log('sending websocket initialisation data.');
websocket.send(JSON.stringify({
'action': 'init',
'data' : {'token': userData.token}
}));
}
the useState that is used above
const [websocketInitialised, setWebsocketInitialised] = React.useState(false);
async function getChatData(userData, setChats, dispatch){
console.log("fetching chat data");
// if we fail to download chat data, pull the old one from FS
const loadOldChatData = async () => {
let chats;
try {
chats = await retrieveChats();
} catch (e) {}
if(chats){
setChats(chats);
console.log("loaded cached chat data") ;
}
else{
setChats([]);
}
};
const onSuccess = (response) => {
if(response['chats']){
storeChats(response['chats']);
setChats(response['chats']);
console.log("chat data synced");
}
else{
loadOldChatData();
}
};
const onFailure = (response) => {
loadOldChatData();
};
fetch(Settings.siteUrl + '/messenger/get_chats/', {
method: "GET",
headers: {
"Content-type": "application/json; charset=UTF-8",
"Authorization": "Token " + userData.token
},
})
.then(response => response.json())
.then(response => {onSuccess(response)})
.catch(response => {onFailure(response)})
}
retrieveUseData() is most likely not the problem as this only started occuring after I added the other code.
Am I not supposed to use states like this or am I supposed to use the async key worked on functions? I tried that but I still have the same issue. You can see on the 4 line of the errors it mentions the 'initialiseWebsocket' function. I am guessing that is the route cause. I assume the solution will be some async version of it...
This error tell us that you didn't or forget to handle error from async code.
I refectory your code a bit, Tell me if you got any error message from console.log(error);
React.useEffect(() => {
// Fetch the token from storage then navigate to our appropriate place
(async () => {
try {
let userData = await retrieveUserData();
dispatch({ type: 'RESTORE_USER_DATA', userData });
await getChatData(userData, setChats, dispatch);
if (websocketInitialised) return;
console.log('web init called from *load user data*')
setWebsocketInitialised(true)
initialiseWebsocket(userData);
} catch (error) {
console.log(error);
dispatch({ type: 'RESTORE_USER_DATA_FAILED' });
}
})();
}, []);
And you should rename getChatData to setChatData, I also simplify those code also...
async function getChatData(userData, setChats, _dispatch) {
try {
let response = await fetch(Settings.siteUrl + '/messenger/get_chats/', {
method: "GET",
headers: {
"Content-type": "application/json; charset=UTF-8",
"Authorization": "Token " + userData.token
},
}),
data = await response.json(),
chats = data['chats'];
if (!chats?.length) throw "empty chat data, pull the old one from FS";
storeChats(chats);
setChats(chats);
} catch (_) {
// if we fail to download chat data, pull the old one from FS
await retrieveChats()
.then(chats => setChats(chats))
.catch(() => setChats([]))
}
}
"I don't really understand what you are doing with the async stuff."
async/await is just syntax sugar of promise, It allow you to work with async operation in a synchronous manner, some rules of async/await
In other to use await keyword, you need an async function.
you can make any function asynchronous, just by adding async keyword
async function always return promise
Lets see an example:
let delay = (ms, msg, bool) => new Promise((res, rej) => setTimeout(!bool ? res : rej , ms,msg));
This helper function create a promise for our example, it take 3 arguments, it take millisecond as 1st arg, to delay, 2rd is the message as payload. 3nd is Boolean; it true, then it will reject.
let delay = (ms, msg, bool) => new Promise((res, rej) => setTimeout(!bool ? res : rej, ms, msg));
let log = console.log;
async function myAsyncFn() {
let hello = await delay(100, "hello,");
let world = await delay(300, " world!");
// we use a symbol '#' to indicate that, its from `myAsyncFn`
log("#" , hello + world, "printed from async operation");
}
myAsyncFn();
log("As you can see that, this message print first");
// we are creating an async function and called immediately, In other to use `await keyword`
(async () => {
try {
let resolved = await delay(300,"resolved");
console.log(">" , `it ${resolved}!`);
// this will reject and catch via `try/catch` block;
let _ = await delay(600, "Error", true);
log("It will not print!");
// ...
} catch (error) {
log(">" , `we can catch "${error}" with try/catch, as like any sync code!`);
}
})()
As you can see that with async/await its look like everything is synchronous right? even everything execute asynchronously!
You just need to use await keyword to make every async operation synchronous.
I have this POST method which uses FetchURL middleware to fetch data from the url submitted by the user.
router.post('/', FetchURL, (req, res) => {
console.info('data received');
...
})
Everything works with response.ok being true, but the contrary case doesn't quite work as expected.
I don't want next to be called when response.ok equals false.
But I get to see "data received" logged to the console which means the next function does get called on its own.
fetch_url.js
function FetchURL(req, res, next) {
fetch(req.body.input_url)
.then(response => {
if(response.ok)
return response.json();
// else render error message on the client machine
res.status(response.status)
.render('index', {
errStatus: [response.status, response.statusText]
});
/* Throwing an Error here is the only way I could prevent the next callback */
// throw new Error(`Request failed with status code ${response.status}.`);
})
.then(data => {
req.data = data;
next();
})
.catch(err => console.error(err));
}
I could not find anything relevant on the documentation of expressjs middleware. The only way I could prevent next from being called is by throwing an Error on the server.
What happens behind the scene here?
try making a second check before next is called like following
function FetchURL(req, res, next) {
fetch(req.body.input_url)
.then(response => {
if(response.ok) // wrap your response in a temporary object.
return { fail: false, data: response.json() } ;
// else render error message on the client machine
res.status(response.status)
.render('index', {
errStatus: [response.status, response.statusText]
});
/* Instead of throwing an Error, return something indicating error */
return { fail: true };
})
.then(data => {
// check if previous procedure has failed.
if(!data.fail) {
req.data = data.data;
next();
}
})
.catch(err => console.error(err));
}
I'd like to make new document by reference of two documents.
**app.post('/student_badge/register', async (req, res) => {
const name = req.body.name;
const category = req.body.category;
People.find({name: name}, '_id', function (err, doc) {
if (err) return handleError(err);
var obj = eval(doc);
id = obj[0]._id;
})
Badge.find({category: category}, 'points title', function (err, doc) {
if (err) return handleError(err);
var obj2 = eval(doc);
points = obj2[0].points;
title = obj2[0].title;
console.log(title + " " + points);
});
data = {
id: id,
title: title,
points: points
}
console.log("data: " + data);
const sbadge = new StudentBadge(data);
sbadge.
save()
.then(result => {
res.status(201).json({
message: 'Post created successfully!',
post: result
});
})
.catch(err => {
console.log(err);
});
});**
But I cannot call three variables like id, title, points to store them in 'data'.
How can I call variables?
Thanks
Your code does not work because the variables you are trying to access, i.e. id, title, points, are being set on a callback function that gets executed asynchronously.
I would suggest using async/await instead of callbacks so that you can thereafter use the data from the other documents you are querying in the same function. In addition, I suggest to use findOne() since you only access the first entry in db.
Something like the example below should work: (I have abstracted the middleware in a separate function for clarity to use with express)
const createStudentBadge = async (req, res, next) => {
const {name, category} = req.body;
let person, badge;
try {
person = await Person.findOne({name}); // shortcut for {name: name}
badge = await Badge.findOne({category});
} catch(err) {
// handle error
}
if (!person || !badge) {
// Handle case where no document has been found in db
// This case will not throw an error when calling find()
}
data = {
id: person._id,
title: badge.title,
points: badge.points
}
const studentBadge = new StudentBadge(data);
try {
await studentBadge.save();
} catch(err) {
// handle error
}
res.status(201).json({
message: 'Post created successfully!',
post: studentBadge
});
}
app.post('/student_badge/register', createStudentBadge);
If you wanted to perform the querying in parallel, you could make use of Promise.all() and run both queries at the same time. More info can be found at MDN documentation
after searching and trying a lot of different things, I find myself in front of this problem : I want to post a content and save the id to an object that belongs in another schema, I'm using mongoose.
Project.findByid is finding the good project and if I log project after modification, the item is as I want it but the save part just doesn't work.
My question is : Is it possible to do a PUT action inside of a POST request, I tried to remove the first .save and it's not working either.
app.post('/pages', (req, res) => {
var db = req.db;
var name = req.body.name;
var parent = req.body.parent;
var myId = mongoose.Types.ObjectId();
var new_content = new Content({
name: name,
_id: myId,
})
new_content.save(function (error, item) {
if (error) {console.log(error)}
})
Project.findById(parent, 'content', function (error, project) {
if (error) { console.error(error); }
project.content[name] = myId;
project.save(function (error) {
if (error) {
console.log(error)
}
res.send({
success: true
})
})
})
})
Simply use
app.all(path, callback [, callback ...])
Instead of
app.post
More info here ExpressJS Docs