Created a basic express.js application and added a model (using thinky and rethinkdb) trying to pass the changesfeed to the jade file and unable to figure how to pass the results of the feed. My understanding is that changes() returns infinite cursor. So it is always waiting for new data. How to handle that in express res. Any idea what am I missing here?
var express = require('express');
var router = express.Router();
var thinky = require('thinky')();
var type = thinky.type;
var r = thinky.r;
var User = thinky.createModel('User', {
name: type.string()
});
//end of thinky code to create the model
// GET home page.
router.get('/', function (req, res) {
var user = new User({name: req.query.author});
user.save().then(function(result) {
console.log(result);
});
//User.run().then(function (result) {
//res.render('index', { title: 'Express', result: result });
//});
User.changes().then(function (feed) {
feed.each(function (err, doc) { console.log(doc);}); //pass doc to the res
res.render('index', { title: 'Express', doc: doc}) //doc is undefined when I run the application. Why?
});
});
module.exports = router;
The problem that I believe you are facing is that feed.eachis a loop that is calling the contained function for each item contained in the feed. So to access the doc contained in console.log(doc) you are going to need to either place your code in the function in which doc exists(is in the scope of the variable doc), or you are going to need to make a global variable to store doc value(s).
So for example, assuming doc is a string and that you wish to place all doc's in an array. You would need to start off by creating a variable which has a scope that res.render is in, which for this example will be MYDOCS. Then you would need to append each doc to it, and after that you would simply use MYDOC anytime you are attempting to access a doc outside of the feed.each function.
var MYDOCS=[];
User.changes().then(function (feed){
feed.each(function (err, doc) { MYDOCS.push(doc)});
});
router.get('/', function (req, res) {
var user = new User({name: req.query.author});
user.save().then(function(result) {
console.log(result);
});
//User.run().then(function (result) {
//res.render('index', { title: 'Express', result: result });
//});
res.render('index', { title: 'Express', doc: MYDOCS[0]}) //doc is undefined when I run the application. Why?
});
module.exports = router;
Related
I am writing a web application to display a web page with content from a PostgreSQL database using NodeJS, express and pg-promise.
I have a database javascript called "db/location.js" which query the location table.
var db_global = require('./db'); # db.js is for building the database connection
var db = db_global.db;
var locationList = [];
// add query functions
module.exports = {
getAllLocationList: getAllLocationList,
locationList: locationList
};
function getAllLocationList() {
db.any('select * from location')
.then(function (data) {
console.log(data);
locationList = data;
}
);
}
In the routes folder, I have a route javascript called "locationRoute.js".
var express = require('express');
var router = express.Router();
var db = require('../db/location');
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
/* GET the map page */
router.get('/locations', function(req, res) {
db.getAllLocationList();
console.log(db.locationList);
res.render('locations', {
title: "Express and Leaflet API", // Give a title to our page
//jsonData: db.getAllLocations // Pass data to the View
jsonData: db.locationList // Pass data to the View
});
});
module.exports = router;
When "http://localhost:3000/locations" is called, this is supposed to render the "locations.jade" which is to display "db.locationList" in a table.
My issue is that "console.log(db.locationList);" is always called before the query was completed. This caused "db.locationList" (jsonData) to be empty.
I don't want to mess the controller layer with the database layer but how to fix the issue?
I think you should change your db/location.js to be something like this...
function getAllLocationList() {
return db.any('select * from location');
}
Then you would do something like this in your routes...
router.get('/locations', function(req, res) {
db.getAllLocationList()
.then(function(data) {
res.render('locations', {
title: "Express and Leaflet API", // Give a title to our page
jsonData: data // Pass data to the View
});
});
...
In your example console.log(db.locationList); runs before the data is available, because it's asynchronous. It doesn't work the way you're expecting it to work.
The code is set up this way:
var express = require('express');
var router = express.Router();
var mongo = require('mongodb').MongoClient;
function getData(){
db.collection("collection_name").find({}).toArray(function (err, docs) {
if (err) throw err;
//doing stuff here
}
var dataset = [
{//doing more stuff here
}
];
});
}
router.get("/renderChart", function(req, res) {
mongo.connect(url_monitor, function (err, db) {
assert.equal(null, err);
getData(res);
});
});
When I run the code and trying to get to /renderChart when running, I get the "ReferenceError: db is not defined". I came across a similar case, and think it may be a similar problem caused because mongodb.connect() is called asynchronously, but I couldn't get it to work:
Express js,mongodb: "ReferenceError: db is not defined" when db is mentioned outside post function
The problem here is you don't pass the db to the function, so it's undefined.
A solution:
function getData(db, res){
db.collection("collection_name").find({}).toArray(function (err, docs) {
if (err) throw err;
//doing stuff here
}
var dataset = [
{//doing more stuff here
}
];
});
}
router.get("/renderChart", function(req, res) {
mongo.connect(url_monitor, function (err, db) {
assert.equal(null, err);
getData(db, res);
});
});
You'll probably need to pass the req at some point too, or make specific db queries. And you'll probably want to use promises or async/await to better deal with all asynchronous calls.
Its Simple Javascript.
You are using a variable db in your file, which is not defined, so it will throw an error.
You need to do something like this .
var findDocuments = function(db, callback) {
// Get the documents collection
var collection = db.collection('documents');
// Find some documents
collection.find({}).toArray(function(err, docs) {
assert.equal(err, null);
assert.equal(2, docs.length);
console.log("Found the following records");
console.dir(docs);
callback(docs);
});
}
I have the same problem before, instead of passing db to routing function, My solution is to make db variable global like
var mongojs = require('mongojs')
global.db = mongojs(<mongodb url>);
then db variable can be used in any part of your code
If you're using express, put that in your app.js file and you will never have to worry about db variable anyore.
PS: some people think that using global is not a good practices, but I argue that since global is a node.js features and especially since it works, why not
node.js global variables?
You don't have tell the codes, that which database you want to use.
how to get databases list https://stackoverflow.com/a/71895254/17576982
here is the sample code to find the movie with name 'Back to the Future' in database sample_mflix > collection movies:
const { MongoClient } = require("mongodb");
// Replace the uri string with your MongoDB deployment's connection string.
const uri =
"mongodb+srv://<user>:<password>#<cluster-url>?retryWrites=true&writeConcern=majority";
const client = new MongoClient(uri);
async function run() {
try {
await client.connect();
const database = client.db('sample_mflix');
const movies = database.collection('movies');
// Query for a movie that has the title 'Back to the Future'
const query = { title: 'Back to the Future' };
const movie = await movies.findOne(query);
console.log(movie);
} finally {
// Ensures that the client will close when you finish/error
await client.close();
}
}
run().catch(console.dir);
to get list of database, put await client.db().admin().listDatabases() on fun function. e.g.
async function run() {
try {
await client.connect();
var databasesList = await client.db().admin().listDatabases();
console.log("Databases:");
databasesList.databases.forEach(db => console.log(` - ${db.name}`));
learn MongoDB more from official docs: https://www.mongodb.com/docs
I have a schema problem. I dont get the right schema in my api. here is my api :
var Meetup = require('./models/meetup');
module.exports.create = function (req, res) {
var meetup = new Meetup(req.body);
meetup.save(function (err, result) {
console.log(result);
res.json(result);
});
}
module.exports.list = function (req, res) {
Meetup.find({}, function (err, results) {
res.json(results);
});
}
The console.log displays { __v: 0, _id: 58343483ff23ad0c40895a00 } while it should display { __v: 0, name: 'Text input', _id: 58343076b80874142848f26e }
here is my model:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Meetup = new Schema({
name: String,
});
module.exports = mongoose.model('Meetup', Meetup);
If req.body is undefined (as you wrote in the comments) then obviously new Meetup(req.body); cannot populate the new objects with any data (like {name: 'Text input'} or anything else) since it is called with undefined as an argument.
Make sure you use the body-parser and that you pass the correct data in your request.
Also, check for errors. Every callback that takes the err argument should be in the form of:
module.exports.list = function (req, res) {
Meetup.find({}, function (err, results) {
if (err) {
// handle error
} else {
// handle success
}
});
}
How to track the problem:
make sure you use the body-parser on the backend
make sure you pass the correct data on the frontend
make sure that the data passed by your frontend is in the correct place (body)
make sure that the data is in the correct format (JSON? URL-encoded?)
add console.log(req.body) after new Meetup(req.body); to know what you save
open the Network tab in the developer console of your browser and see what is transferred
I am trying to pass some data from my db to the router which then passes the data to the view.
My model code :
var mysql = require('mysql');
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '',
database: 'test'
});
var result; // empty var which should later be filled with the querys result
connection.connect();
var query = connection.query('SELECT * FROM users', function(err, res, fields) {
if (err) throw err;
result = res; // overwrite result with the querys result
console.log(res); // This prints out everything I need
});
module.exports = {
data: result // should contain the query result (= 2 objects in this case)
}
Now to my route file :
var express = require('express');
var router = express.Router();
var Users = require('../models/users');
console.log(Users.data);
/* GET home page. */
router.get('/users', function(req, res) {
res.render('api', { data: Users.data, title: "Test API Output" });
});
module.exports = router;
When I console.log Users or Users.data I get undefined. I don't really get why this is the case. How else am I supposed to pass data along the files.
All help is gladly read :) Thank you.
module.exports are being evaluated the second you require and variables are not passed by reference in this case.
What that means for your code is the following:
var result; // result is "undefined" because it does not contain a value here
// You are doing your DB queries here...
module.exports = {
data: result // ...and because the query has not finished here yet, result
// is still undefined.
// This is also a good example of a so called "race condition", because there is a
// slight (improbable) chance that the query might have already finished.
// Hence, it could happen that sometimes result is already filled.
}
When you now require the above file in another file of your code, the above is being evaluated and saved straight away (result is undefined at that point in time, hence it is also undefined when it exports).
Your query is being executed and written into the result variable, but at that point in time you can not modify the exported variable anymore – because it is it's own variable and not merely a reference to result).
What you could do is the following:
function getData(callback) {
connection.query('SELECT * FROM users', function(err, res, fields) {
callback(err, res);
});
}
module.exports = {
getData: getData
}
and then in your other file:
var Users = require('../models/users');
Users.getData(function(err, result) {
// TODO: Error handling.
console.log(result);
});
That's exactly why it's so easy with JavaScript to end up in callback hell, because of it's asynchronous nature.
The above is the exact same situation as if you, f.e., want to get some data via AJAX from a server and then fill tables with it. When you start creating the table before you have the data (so the AJAX request is not yet complete), you end up with an empty table. What could do is:
you create a variable that holds your data and
a function that creates the table
when you then ask the server for the data (via AJAX) you wait until you get the data (completion callback) and only then you start creating the table: filling your variable and calling the function to fill the table with the data.
Server-Side JavaScript is the same as client-side. Never forget this.
As a little homework for the reader: the way to get out of callback hell is by reading up on promises – a pattern/architecture which reduces indents and saves lots of headaches :)
(update: Lucas' answer is basically telling the same thing as I did)
(update 2: wrong way of handling err)
I suggest realize the consult in the route file, some like this:
var express = require('express');
var router = express.Router();
var Users = require('../models/users');
var mysql = require('mysql');
var connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '',
database: 'test'
});
var result; // empty var which should later be filled with the querys result
connection.connect();
/* GET home page. */
router.get('/users', function(req, res) {
var query = connection.query('SELECT * FROM users', function(err, res, fields) {
if (err) throw err;
result = res; // overwrite result with the querys result
res.render('api', { data: res.data, title: "Test API Output" });
});
});
module.exports = router;
But you can configure the connection with database in another file, in libs/mysql_connect.js.
The undefined is caused because the response of connection.query don't works out of the connection.query.
If you really want the query to run only once and then just re-use the already queried data, I think you are after something like this for your model:
...
var data;
var mymodel = {};
...
mymodel.getData = function(callback) {
if(data) {
callback(data);
} else {
db.query('select * from users', function(err,res,fields) {
// error checking and such
data = res;
callback(data);
});
}
}
module.exports = mymodel
In your router, you'd then use it like this:
...
router.get('/users', function(req, res) {
Users.getData(function(mydata) {
res.render('api', { data: mydata, title: "Test API Output" });
});
});
The first time you call getData, you'll get a fresh result, and on subsequent calls you get the cached result.
While you could expose data in mymodel directly, and only use the callback in case it is still undefined, that'd make your code in the router more convulated.
I am using Node.js, Express, MongoDB, and Mongoose. I have a function that fetches the largest id number of a document in my MongoDB database and returns it to the program. I have begun modularizing my code, and have migrated that function to another module. I have successfully accessed the function in my main module, but it involves an asynchronous database query. As the function returns a value, I want to assign it to a variable. Unfortunately, When the returned value is assigned to the variable, the variable is actually set to undefined. I was thinking about using event emitters to signal that the query is finished, but that presents two issues as well:
1) I don't think you can do anything in a program AFTER a return statement, which would be what is required.
2) Event Emitters between modules seem very finicky.
Please help me get the variable to be assigned to the correct value. Code for both the main function and the module is below:
(main file) app.js:
//requires and start up app
var express = require('express');
var mongoose = require('mongoose')
, dbURI = 'localhost/test';
var app = express();
var postmodel = require('./models/post').postmodel;
//configures app for general stuff needed such as bodyParser and static file directory
app.configure(function () {
app.use(express.bodyParser());
app.use(express.static(__dirname + '/static'));
});
//configures app for production, connects to mongoLab databse rather than localhost
app.configure('production', function () {
dbURI = 'mongodb://brad.ross.35:lockirlornie#ds037387.mongolab.com:37387/heroku_app6901832';
});
//tries to connect to database.
mongoose.connect(dbURI);
//once connection to database is open, then rest of app runs
mongoose.connection.on('open', function () {
var PostModel = new postmodel();
var Post = PostModel.setupPostSchema();
var largest_id = PostModel.findLargestID(Post);
(module) post.js:
var mongoose = require('mongoose');
module.exports.postmodel = function () {
this.setupPostSchema = function () {
var postSchema = new mongoose.Schema({
title: String,
body: String,
id: Number,
date_created: String
});
var Post = mongoose.model('Post', postSchema);
return Post;
};
this.findLargestID = function (Post) {
Post.find(function (err, posts) {
if (err) {
console.log("error finding largest ID!");
} else {
var largest_id = 0;
for (var post in posts) {
if (posts[post].id >= largest_id) largest_id = posts[post].id;
}
console.log(largest_id);
return largest_id;
}
});
};
};
You need to have findLargestID accept a callback parameter that it will call once largest_id is available:
this.findLargestID = function (Post, callback) {
Post.find(function (err, posts) {
if (err) {
console.log("error finding largest ID!");
callback(err);
} else {
var largest_id = 0;
for (var post in posts) {
if (posts[post].id >= largest_id) largest_id = posts[post].id;
}
console.log(largest_id);
callback(null, largest_id);
}
});
};