mongodh + hapi.js: Collection is not defined? - javascript

I am new to mongodb and Hapi.js. I am trying to create an API for read requests, but am not sure how to write the handler method in server.route.
Here's how I have my mongoclient configured with hapi:
'use strict';
var MongoClient = require('mongodb').MongoClient; //using version 3.x
var Hapi = require('hapi');//using v16
var url = 'mongodb://****:****#ds131687.mlab.com:31687/learning_mongo';
var db;
var server = new Hapi.Server();
server.connection({
port:8080
});
server.route( [
// Get tour list
{
method: 'GET',
path: '/api/tours',
handler: function(request, reply){
collection.find().toArray(function(err,tours){
reply(tours);
});
}
},
// Home page
{
method: 'GET',
path: '/',
handler: function(request, reply) {
reply( "Hello world from Hapi/Mongo example.");
}
}
]);
var tours = function(db, callback) {
var collection = db.collection('tours');
collection.find().toArray(function(err, docs){
console.log(docs);
callback;
});
};
MongoClient.connect(url, function(err,client) {
server.start(function(err) {
tours(client.db('learning_mongo'), function(){
console.log('Hapi is listening to http://localhost:8080');
client.close();
});
});//end server
})
Going to the homepage path works fine, but when I go to ./api/tours path, I get the following error in terminal:
Debug: internal, implementation, error
ReferenceError: Uncaught error: collection is not defined
at handler (/home/ubuntu/workspace/index.js:22:13)
at Object.internals.handler (/home/ubuntu/workspace/node_modules/hapi/lib/handler.js:101:51)
at request._protect.run (/home/ubuntu/workspace/node_modules/hapi/lib/handler.js:32:23)
at module.exports.internals.Protect.internals.Protect.run (/home/ubuntu/workspace/node_modules/hapi/lib/protect.js:60:12)
at exports.execute (/home/ubuntu/workspace/node_modules/hapi/lib/handler.js:26:22)
at each (/home/ubuntu/workspace/node_modules/hapi/lib/request.js:401:16)
at iterate (/home/ubuntu/workspace/node_modules/items/lib/index.js:36:13)
at done (/home/ubuntu/workspace/node_modules/items/lib/index.js:28:25)
at module.exports.internals.Auth.internals.Auth._authenticate (/home/ubuntu/workspace/node_modules/hapi/lib/auth.js:222:16)
at internals.Auth.authenticate (/home/ubuntu/workspace/node_modules/hapi/lib/auth.js:197:17)
How do I correctly define the collection ? Thank you.

Your error message means that collection is out of scope inside the handler. You declare it inside the tours function.
But you also got have a minor error how you approach the database and the collection with the Mongoclient.
Let me show you how it would work while keeping your general set-up. There you can see that db can be accessed by the handler now.
'use strict';
var MongoClient = require('mongodb').MongoClient; //using version 3.x
var Hapi = require('hapi'); //using v16
var url = 'mongodb://****:****#ds131687.mlab.com:31687/';
var db;
var server = new Hapi.Server();
server.connection({
port: 8080
});
server.route([
// Get tour list
{
method: 'GET',
path: '/api/tours',
handler: function(request, reply) {
db.collection('tours').find().toArray(function(err, tours) {
reply(tours);
});
}
},
// Home page
{
method: 'GET',
path: '/',
handler: function(request, reply) {
reply("Hello world from Hapi/Mongo example.");
}
}
]);
var tours = function(db, callback) {
db.collection('tours').find().toArray(function(err, docs) {
console.log(docs);
callback;
});
};
new MongoClient.connect(url, function(err, client) {
db = client.db('learning_mongo')
server.start(function(err) {
tours(db, function() {
console.log('Hapi is listening to http://localhost:8080');
client.close();
});
}); //end server
})
I understand that this is only a learning example from your side. But maybe you want to consider starting with the latest hapijs version: 17. There are some bigger changes involved and it makes your life easier starting with that version now. Your short code has already lot of nested callbacks. Version 17 will support using await/async.

Related

Rethinkdb changesfeed in Express.js using thinky

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;

passing parameters to module.exports in nodejs

I have the following code for implemetation of nodejs a rest api.
app.js
var connection = require('./database_connector');
connection.initalized(); //guys connection is i want to pass a connection varible to the model
var peson_model = require('./models/person_model')(connection); //this not working
var app = express();
app.use(bodyparser.urlencoded({extended: true}));
app.use(bodyparser.json());
app.get('/persons/', function(req, res) {
person_model.get(res); // retrive get results
});
// .............express port and listen
person_model.js is a model class that is supposed to retrieve based on the http verb. For example person.get retrieves the following and currently has a single method as follow.
function Person(connection) {
this.get = function (res) {
connection.acquire(function(err, con) {
con.query('select * from person limit 3', function(err, result) {
con.release();
console.log("get called");
res.send(result);
});
});
};
}
// ** I want to pass a connection variable to the model
module.exports = new Person(connection);
In the code above, var peson_model = require('./models/person_model')(connection); is not working.
How do I pass the connection variable and export the module?
If you return a function from your export, you can pass your parameter.
module.exports = function(connection) {
return new Person(connection);
};
You will need to set this.connection and use that inside your function though.

Registering Glass Timeline Notification with Node

I'm trying to register TAKE_A_NOTE with the 'mirror-api-subscription' event listener. I'm not having any luck: I can launch take a note with "my app", however, there's no console log that the event has been recognized.
I would like to recognize when the TAKE_A_NOTE function has occurred or finished, and handle the response afterwards. I do not know whether the subscription should be on a stream or if I am implementing the EventListener in a faulty manner. Your help would be greatly appreciated.
The code I am utilizing is:
// references
// http://www.recursiverobot.com/post/57348836217/getting-started-with-the-mirror-api-using-node-js
// https://www.npmjs.org/package/mirror-api-subscription
var express = require('express')
, http = require('http')
, https = require('https')
, fs = require('fs')
, googleapis = require('googleapis')
, OAuth2Client = googleapis.OAuth2Client;
var app = express();
var oauth2Client = new OAuth2Client(process.env.MIRROR_DEMO_CLIENT_ID,
process.env.MIRROR_DEMO_CLIENT_SECRET, process.env.MIRROR_DEMO_REDIRECT_URL);
// all environments
app.set('port', 8888);
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
var gotToken = function () {
googleapis
.discover('mirror', 'v1')
.execute(function (err, client) {
if (!!err) {
failure();
return;
}
insertContact(client, failure, success);
insertSubscription(client, failure, success);
});
};
var insertContact = function (client, errorCallback, successCallback) {
client
.mirror.contacts.insert({
"id": "myapp",
"displayName": "myApp",
"priority": 100,
"acceptCommands": [
{"type": "TAKE_A_NOTE"}
],
"speakableName":"my app"
})
.withAuthClient(oauth2Client)
.execute(function (err, data) {
if (!!err)
errorCallback(err);
else
successCallback(data);
});
};
var insertSubscription = function (client, errorCallback, successCallback) {
client.mirror.subscriptions.insert({
"callbackUrl":"https://localhost:7777/notification",
"collection":"timeline",
"userToken":"001",
"verifyToken":"secret",
"operation":["INSERT"]
});
}
var subscription = require('mirror-api-subscription')(
function () {
})
subscription.on('locations#UPDATE',
function (notification, user, payload) {
console.log('location of user %s updated', user.id)
})
subscription.on('timeline#INSERT:LAUNCH',
function (notification, user, payload) {
console.log('subscription timeline#INSERT:LAUNCH')
})
subscription.on('timeline#UPDATE:CUSTOM',
function (notification, user, payload) {
console.log('subscription timeline#UPDATE:CUSTOM')
})
app.post('/notification', subscription.dispatcher())
app.get('/', function (req, res) {
if (!oauth2Client.credentials) {
// generates a url that allows offline access and asks permissions
// for Mirror API scope.
var url = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: 'https://www.googleapis.com/auth/glass.timeline'
});
res.redirect(url);
} else {
gotToken();
}
res.write('Glass Mirror API with Node');
res.end();
});
app.get('/oauth2callback', function (req, res) {
// if we're able to grab the token, redirect the user back to the main page
grabToken(req.query.code, failure, function () {
res.redirect('/');
});
});
app.post('/reply', function(req, res){
console.log('replied',req);
res.end();
});
var options = {
key: fs.readFileSync('./ssl/key.pem'),
cert: fs.readFileSync('./ssl/cert.pem'),
};
https.createServer(options, app).listen(7777, function() {
console.log('https listening on 7777');
});
http.createServer(app).listen(app.get('port'), function () {
console.log('Express server listening on port ' + app.get('port'));
});
There are at least two potential problems with your code:
The callbackUrl must be an internet accessible HTTPS address. "Localhost" isn't good enough, since Google's servers need to be able to resolve it. You can provide an IP address, if you have a public IP address, or use a tunnel.
You don't do anything with the result of the call to client.mirror.subscriptions.insert(). Typically, you should call it the same way you call client.mirror.contacts.insert(), which is to chain it with withAuthClient() and execute(). You'll need to call execute() if you expect it to register with the Mirror service. See https://github.com/google/google-api-nodejs-client/ for documentation about the "googleapis" package and discovery service
Possibly related to #2, but I'm not familiar with the package you're including with require('mirror-api-subscription'), which seems to handle things differently than the "googleapis" package and discovery service it offers. From the reference documentation for it, however, it isn't clear that it actually sets up the callback anywhere and is just there to verify the callbacks and dispatch to functions that do the work.
var insertSubscription = function (client, errorCallback, successCallback) {
client.mirror.subscriptions.insert({
"callbackUrl":"https://mirrornotifications.appspot.com/forward?url=callbackURL",
"collection":"timeline",
"userToken":"001",
"verifyToken":"secret",
"operation":["INSERT"]
}).withAuthClient(oauth2Client).execute(function (err, data) {
if (!!err)
errorCallback(err);
else
successCallback(data);
});
};

Node.js : Execute a rest call inside if statement

I am using Node JS with express and I want to create a new user in my db with a REST call if the user logs in successfully with facebook. This code is just a slight modification of the heroku node.js facebook app : https://github.com/heroku/facebook-template-nodejs/blob/master/web.js
Here's what i've tried
//create Parse Object
var parse_app_id = process.env.PARSE_APP_ID;
var parse_master_key = process.env.PARSE_MASTER_KEY;
var nodeParse = new Parse(parse_app_id, parse_master_key);
function handle_facebook_request(req, res) {
// if the user is logged in
if (req.facebook.token) {
async.parallel([
function(cb) {
// query 4 friends and send them to the socket for this socket id
req.facebook.get('/me/friends', { limit: 4 }, function(friends) {
req.friends = friends;
cb();
});
},
function(cb) {
// query 16 photos and send them to the socket for this socket id
req.facebook.get('/me/photos', { limit: 16 }, function(photos) {
req.photos = photos;
cb();
});
}
], function() {
render_page(req, res);
nodeParse.signUp('',{ username: 'tajmahal', password: 'stuff'}, function(err, response) {
console.log(response);
});
});
}
}
app.get('/', handle_facebook_request);
app.post('/', handle_facebook_request);
handle_facebook_request is executed when the app is requested...everything works fine besides the user creation.
signUp creates a user when its outside of the conditional (so the function works)
How can I fix this?

Node.js Making a Variable Wait to be Assigned Until Callback Function is Done

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);
}
});
};

Categories

Resources