I have an App written using the HapiJs framework for Node and want to connect it to a CouchDb databse, but am having trouble finding the code to do so.
Can anyone help me with the code to do that? What is the 'normal' way of doing that?
Cheers!
Well you don't need any framework for couchdb. Everything is available via a rest api. Just use request module to make requests to the api. A few examples : -
Read a document
request.get("http://localhost:5984/name_of_db/id_of_docuement",
function(err,res,data){
if(err) console.log(err);
console.log(data);
});
Read from a view
request.get(
"http://localhost:5984/name_of_db/_design/d_name/_view/_view_name",
function(err,res,data){
if(err) console.log(err);
console.log(data);
});
The entire api is documented here
There is no need to manage connections or to handle the opening and closing of database that you might be doing with other databases. Simply start couchdb and start making requests to from your application.
However if you find that making requests to the api directly is a bit cumbersome for you, then you can try using nano which provides a nicer syntax for doing things with couchdb.
Some snippets of code
All right so I am not familliar with hapi so I will just tell you how do do it with request.
Consider this example from the docs
var Hapi = require('hapi');
var server = new Hapi.Server(3000);
var request = require("request");
server.route({
method: 'GET',
path: '/',
handler: function (request, reply) {
reply('Hello, world!');
}
});
server.route({
method: 'GET',
path: '/{name}',
handler: function (req, rep) {
request.get("http://localhost:5984/name_of_db/id_of_docuement",
function(err,res,data){
if(err) console.log(err);
rep(data);
});
}
});
server.start(function () {
console.log('Server running at:', server.info.uri);
});
When you call the / endpoint it the request handler for it is executed. It makes a request to a couchdb endpoint to fetch a a document. You don't need anything to connect to couchdb besides that.
Another option could be the hapi-couchdb plugin (https://github.com/harrybarnard/hapi-couchdb).
Using it is a little more "hapi-like" than making direct calls into the Couch API directly.
Here's an example from the plugin documentation:
var Hapi = require('hapi'),
server = new Hapi.Server();
server.connection({
host: '0.0.0.0',
port: 8080
});
// Register plugin with some options
server.register({
plugin: require('hapi-couchdb'),
options: {
url: 'http://username:password#localhost:5984',
db: 'mycouchdb'
}
}, function (err) {
if(err) {
console.log('Error registering hapi-couchdb', err);
} else {
console.log('hapi-couchdb registered');
}
});
// Example of accessing CouchDb within a route handler
server.route({
method: 'GET',
path: '/',
handler: function (request, reply) {
var CouchDb = request.server.plugins['hapi-couchdb'];
// Get a document from the db
CouchDb.Db.get('rabbit', { revs_info: true }, function(err, body) {
if (err) {
throw new Error(CouchDb.Error(error); // Using error decoration convenience method
} else {
reply(body);
});
}
});
server.start(function() {
console.log('Server running on host: ' + server.info.uri);
});
Related
I'm writing a REST API in Sails.js and alongside the regular HTTP routes, I need the application to listen for notifications on a socket from Salesforce.
I have a controller with some logic but I don't know how to get it to subscribe to the socket on startup, so right now nothing is reaching it.
Controller:
pushTopicHandler: function(req, res) {
if (!req.isSocket) {
return res.badRequest();
}
var nforce = require('nforce');
var org = nforce.createConnection({
clientId: sails.config.client_id,
clientSecret: sails.config.client_secret,
redirectUri: sails.config.callback_url + '/oauth/_callback',
mode: 'multi',
environment: 'sandbox'
});
org.authenticate({ username: sails.config.sfUsername, password: sails.config.sfPassword }, function(err, oauth) {
if(err) return res.serverError(err);
var str = org.stream({ topic: sails.config.push_topic, oauth: oauth });
str.on('connect', function(){
console.log('Connected to pushtopic: ' + sails.config.push_topic);
});
str.on('error', function(error) {
console.log('Error received from pushtopic: ' + error);
});
str.on('data', function(data) {
console.log('Received the following from pushtopic ---');
console.log(data);
});
});
}
Sails has a bootstrap.js file which lets you write anything you want to run before the server lifts.
I was able to subscribe to the push topic in a small function before the cb() in that file and it works, the server starts the REST API normally and it's still listening for events.
I have to do request from node to Yii2 api. It doesn't throw any errors, but doesn't return anything either. When I do request to Yii2 api method directly in browser, value is returned. Here is my request in route in node:
router.get('', function (req, res) {
var parameter = 20;
request({
url: 'http://**.**.**.***:8000/web/index.php?r=api/get-value',
parameter: parameter,
method: 'GET'
}, function(error, response, body) {
if(error || response.statusCode != 200)
throw error;
res.send(body);
});
});
module.exports = router;
And here is method/endpoint in Yii2 controllers/apiController.php:
public function actionGetValue($inverterId) {
return $inverterId * 2;
}
Any suggestions what could be wrong/missing?
You can use the following
var http = require('http');
var client = http.createClient(8000, 'localhost');
var request = client.request('GET', '/web/index.php?r=api/get-value');
request.write("stuff");
request.end();
request.on("response", function (response) {
// handle the response
});
Resource Link:
Http request with node?
Sending http request in node.js
or Another full example:
Get requests
Now we’ll set up a super simple test to make sure it’s working. If it’s not still running, run your simple Node server so that it’s listening on http://localhost:8000. In a separate file in the same directory as your http-request.js where your new module lives, add a file called test-http.js with the following contents:
// test-http.js
'use strict';
const
request = require('./http-request'),
config = {
method: 'GET',
hostname: 'localhost',
path: '/',
port: 8000
};
request(config).then(res => {
console.log('success');
console.log(res);
}, err => {
console.log('error');
console.log(err);
});
This will import our module, run a request according to the configured options, and console log either the response, or an error if one is thrown. You can run that file by navigating to its directory in the command line, and typing the following:
$ node test-http.js
You should see the following response:
success
{ data: 'Cake, and grief counseling, will be available at the conclusion of the test.' }
Resource Link:
https://webcake.co/sending-http-requests-from-a-node-application/
Okay, shame on me, I did not check, what's going on in public function beforeAction($action) in apiController.php - since request to endpoint getValue() is done from the "outside", it falls under a condition, that does not allow further actions and returns false - that's why response wasn't changing no matter what was done/set in getValue().
I successfully generated an access token but now I can't use it. Sadly there is no official yandex node library for using the API. There are 2 unofficial npm module that will only work with a server but I want it localhost. As example with code like this, in this case I want to display all files from my disk. Of course I enabled all scopes for my app
request.get('https://cloud-api.yandex.net/v1/disk/resources/files/', {
'auth': {
'bearer': 'xxxxxxxxxxxxxxxxxxx'
}
}, function(err,httpResponse,body){ /* ... */
if(err) {
console.log('err: ' + err)
}
console.log('body: ' + body)
});
Also if I would use https://cloud-api.yandex.net/v1/disk/resources/files?oauth_token=xxxxxxxxxxxxxx&oauth_client_id=xxxxxxxxxxxxxxxxxxx
or https://cloud-api.yandex.net/v1/disk/resources/files?access_token=xxxxxxxxxxxxxx&client_id=xxxxxxxxxxxxxxxxxxx
in my browser I would get
{"message":"?? ???????????.","description":"Unauthorized","error":"UnauthorizedError"}
Somebody has working code or an idea why I´am getting this message?
Now I'm using yandex-disk package. It works enougth for my purposes.
That my steps:
Register app in Yandex Oath Service.
After that I can get info about my app
Realize the access method to get Oath Token by calling this.
npm i yandex-disk
Inserting the code into index.js
const YandexDisk = require('yandex-disk').YandexDisk;
const disk = new YandexDisk('YourOathTokenHere');
console.log('disk', disk);
disk.readdir('/', (err, response) => {
console.log('callback error', err);
console.log('callback response', response);
});
Just include authorization header like that:
axios.get(encodeURI(`https://cloud-api.yandex.net:443/v1/disk/resources?path=${path}`), {
headers: {
'Authorization': `OAuth ${token}`
}
}).then((res) => {
console.log(res);
}).catch((err) => {
console.log(err);
});
I am creating a small project to become familiar with NodeJS, Express and the MEAN stack in general. I'm also very new to web development.
I want to add search functionality to my little site. I have a controller, web service and a javascript file that contains all my database calls (MongoDB).
My question is: How do you pass the search value entered by the user from the web service to the route and then to the db? I've looked everywhere and but I have been unable to find a concrete example. This is what I've got so for.
My controller calls my web service.
this.search = function(searchValue,callback) {
console.log(searchValue);
$http({
method: 'GET',
url: '/contacts/search/:searchValue',
params: {searchValue: searchValue},
headers: {'Content-type': 'application/json'}
}).success(function(resp){
console.log("here");
callback(resp);
}).error(function(){
callback(undefined);
});
};
Next, my web service calls my route...
router.get('/search/:searchValue', function(req, res) {
db.search(req.params.searchValue, function(err,data){
if(!err) {
res.json(data);
}else{
res.json({code:-1,err:err});
}
});
});
Then the database call...
database.prototype.search = function(id,callback){
mongo.connect(dbUrl, function(err, db) {
if(!err) {
db.collection('friends',function(err,coll){
coll.find({friend:"Jimmy"}).toArray(function(err, items) {
db.close();
callback(null,items);
});
});
}else{
db.close();
console.log("hi");
callback(err,null);
}
});
};
Things work fine when I hard code my search value right into my db call (ie. "Jimmy" above). However, I don't know how to pass the search value from my web service to my route and then to the db. I get errors like the route cannot be found or I cannot connect to the database. Silly things that go away when I hard code values.
Anyhow, thank you for your time and patience.
In your router (what you call your web service) you're calling your database search function like this:
router.get('/search/:searchValue', function(req, res) {
db.search(req.params.searchValue, function(err,data){
...
Notice you're passing to your db.search req.params.searchValue
But in your database you have your same search function defined as:
database.prototype.search = function(id, callback){...
which as you can see, takes id as argument.
There's clearly a disconnect here. You're passing it the searchValue from router but you've defined it to take id.
Then further down in database search function you doing this:
database.prototype.search = function(id,callback){
mongo.connect(dbUrl, function(err, db) {
if(!err) {
db.collection('friends',function(err,coll){
coll.find({friend:"Jimmy"}).toArray(function(err, items) {
...
you're calling coll.find to which you should presumably want to pass that searchValue. There's another disconnect here, you're never using the id that you took as a parameter.
If you say that for things to work fine all you gotta do is put "Jimmy", which I guess is the searchValue, then you should try this:
database.prototype.search = function(searchValue,callback){ // replace id with searchValue
mongo.connect(dbUrl, function(err, db) {
if(!err) {
db.collection('friends',function(err,coll){
// use searchValue here
coll.find({friend:searchValue}).toArray(function(err, items) {
...
edit
There's some problem on your client side code as well
$http({
method: 'GET',
url: '/contacts/search/:searchValue',
params: {searchValue: searchValue},
headers: {'Content-type': 'application/json'}
Here you're making an AJAX call with Angular's $http module. And you're making a GET request but you're passing params along with it, which is usually only passed when you make a POST request. Also, the way you've defined your route you're only reading searchValue from the URL itself. So the URL here should be like this:
$http({
method: 'GET',
url: '/contacts/search/' + searchValue,
// params: { no need for params }
...
To explain a bit more how URL params work:
If you request a URL like this
GET /contacts/search/abcd
then you'd define your Express route handler like this
app.get('/contacts/search/:name', function(req, res, next){
req.params.name //=> "abcd"
});
Notice the syntax of route definition /contacts/search/:name is only used for defining the route in Express.
The /:name part is just to assign a variable name - name to the value - "abcd" so it could be accessed as req.params.name
Which is why this is wrong
$http({
method: 'GET',
url: '/contacts/search/:searchValue',
it should be this
$http({
method: 'GET',
url: '/contacts/search/' + yourActualSearchValue,
Coming from the world of express, if there was an error when compiling a Jade template, the error would output to the browser with details of the template error including line number. I would like to see this level of error detail in Hapi.js when the view template compiler encounters an error.
Instead, with Hapi.js, I receive a 500 Internal Server Error instead. The only output I see in the logs is the following:
150511/005652.910, [response], http://localhost:3000: get /abc123 {} 500 (24ms)
This is the basics of my setup.
var Hapi = require('hapi');
var server = new Hapi.Server();
server.connection({ port: 3000 });
server.views({
engines: { jade: require('jade') },
path: __dirname + '/views',
compileOptions: {
pretty: true,
debug: true,
compileDebug: true
}
});
server.route({
method: 'GET',
path: '/{name}',
handler: function (request, reply) {
reply.view('page', {name: request.params.name});
}
});
// I know putting my plugins in an array is not necessary but I like it.
var plugins = [{
register: Good,
options: {
reporters: [{
reporter: require('good-console'),
events: {
response: '*',
log: '*'
}
}]
}
}];
server.register(plugins, function (err) {
if (err) {
throw err; // something bad happened loading the plugin
}
server.start(function () {
server.log('info', 'Server running at: ' + server.info.uri);
});
});
Had the same problem this week too.
var server = new Hapi.Server({
debug: {
request: ['error']
}
});
It won't output to the client (you'll still get a 500 error), but the compilation error will show up in the terminal console.
One possible solution is to log on server 'request-error'. For instance:
server.on('request-error', function (request, err) {
//logs the object
server.log('error', err);
//logs the view compiler error line number and details
server.log('error', err.toString());
});
I would still prefer to see this in the browser (while in "development mode") in addition to the logs.
Because the rendering of the template happens after onPreResponse, it's not possible to catch the error at that point. A way of sending the error to the browser, albeit slightly hacky, is to do a dry run of the compilation inside an extension point, and then transmit the error to the browser at that point:
server.ext('onPreResponse', function (request, reply) {
var response = request.response;
if (response.variety === 'view') {
var source = response.source;
// Let's pre-render the template here and see if there's any errors
return server.render(source.template, source.context, function (err) {
if (err) {
return reply(err.message); // transmit the compile error to browser
}
reply.continue();
});
}
reply.continue();
});
Obviously this has a performance impact because you're rendering the view twice under normal conditions, so you'll want to disable it in production.