I am writing my first node.js package to help make a REST API easier to use and I am having trouble formatting the package to allow the person using the package to do the below in their applications.
var Water= require("water-reservation");
var water = Water({username: myusername, password: mypassword});
// Problem here because Water({}) needs to do a REST call to get a bearer
// token to use the API I am trying to simplify. Thus, it tries to
// get_water(callabck) before the bearer_token var is set in my package.
water.get_water(function(err, result){
if(err){
console.log(err);
console.log("----------------------------------------------");
}
else{
console.log(result);
console.log("----------------------------------------------");
}
});
In my current setup I take the user's username and password and pass it to a REST endpoint to get a bearer token that I use in all my REST calls for the package. The async nature will call get_water in the users file before I am done with setting the bearer_token variable in my package.
Here is what my package looks like:
var request = require('request');
var bearer_token = "";
var api_url = "";
var Water = function(credentials){
api_url = credentials.api_url;
var username = credentials.username;
var password = credentials.password;
get_bearer_token(username, password, function(err, access_token){
bearer_token = access_token;
});
};
function get_bearer_token(username, password, callback){
var request_options = {
url: api_url + "/auth",
method: "GET",
'auth': {
'user': username,
'pass': password
}
};
request(request_options, function(err, res, body){
if(err) {
return callback("Water Initialization Error: " + err, null);
}
else {
return callback(null, body);
}
});
}
// Get water
Water.prototype.get_water = function(callback) {
var request_options = {
url: api_url + "/water",
method: "GET",
'auth': {
'bearer': bearer_token
}
};
request(request_options, function(err, res, body){
if(err) {
return callback(err, null);
}
else{
return callback(null, body);
}
});
};
// more package functions...
module.exports = Water;
I am trying to find a way to make the setup so the user can use the package as described above. I am not set on that style, but it seems to be the easiest for a user to understand and use.
The only way I could think of fixing this is add a callback to Water({}) but that would make a lot of mess having the user wrap all their water related code in the callback. I know it can be done by looking at the Twitter package for example, but I still could wrap my mind around how they did it. Any help would be appreciated, thanks.
You should delegate the responsibility of getting the token to when they make a request. That way the package is initialised instantly. To make it more efficient you can cache the token the first time it is fetched. I've made an example here, but you could tidy it up using something like async:
var request = require('request');
var Water = function (credentials) {
this.api_url = credentials.api_url;
this.username = credentials.username;
this.password = credentials.password;
return this;
};
Water.prototype.get_bearer_token = function (callback) {
// We already have the bearer token, so return straight away
if (this.bearer_token) return callback(null, this.bearer_token);
var self = this;
var request_options = {
url: this.api_url + "/auth",
method: "GET",
'auth': {
'user': this.username,
'pass': this.password
}
};
request(request_options, function(err, res, body){
if(err) {
return callback("Water Initialization Error: " + err, null);
}
else {
self.bearer_token = body;
return callback(null, body);
}
});
}
// Get water
Water.prototype.get_water = function(callback) {
var self = this;
this.get_bearer_token(function (err, token) {
if (err) return callback(err);
var request_options = {
url: self.api_url + "/water",
method: "GET",
'auth': {
'bearer': token
}
};
request(request_options, function(err, res, body){
if(err) {
return callback(err, null);
}
else{
return callback(null, body);
}
});
});
};
// more package functions..
module.exports = Water;
To use it:
var Water = require('water-reservation');
var water = new Water({
username: 'username',
password: 'pass',
api_url: 'http://example.com'
});
water.get_water(function (err, res) {
});
Related
I am creating a 'refresh data' function in Node and I cannot figure out where to place the callbacks and returns. The function continues to run. Below is a list of things the function should do. Could someone help out?
Check if a user has an api id in the local MongoDB
Call REST api with POST to receive token
Store token results in a MongoDB
Terminate function
./routes/index.js
router.post('/refresh', function(req, res) {
var refresh = require('../api/refresh');
refresh(req, function() { return console.log('Done'); });
});
../api/refresh.js
var callToken = require('./calltoken');
var User = require('../models/user'); // Mongoose Schema
module.exports = function(req, callback) {
User.findOne( {'username':req.body.username}, function(err, user) {
if(err) { console.log(err) }
if (user.api_id == 0) {
callToken.postToken(req.body.username, callback);
} else { // Do something else }
});
};
./calltoken.js
var request = require('request');
var Token = require('../models/token'); // Mongoose Schema
module.exports = {
postToken: function(user, callback) {
var send = {method:'POST', url:'address', formData:{name:user} };
request(send, function(err, res, body) {
if(err) { console.log(err) }
if (res.statusCode == 201) {
var newToken = new Token();
newToken.token = JSON.parse(body).access_token['token'];
newToken.save(function(err) {
if(err) { console.log(err) }
return callback();
});
}
});
}
};
I'm not an expert in Express but everywhere in you code in lines with if(err) { console.log(err) } you should stop execution (maybe of course not - up to you app) and return 400 or 500 to client. So it can be something like
if(err) {
console.log(err);
return callback(err); // NOTICE return here
}
On successful execution you should call return callback(null, result). Notice null as a first argument - it is according nodejs convention (error always goes as first argument).
Probably an obvious answer to this but I'm not sure what way to take.
request is a node module: https://github.com/request/request
I fill an array of getHistory requests (with different parameters). p = [p1,p2...].
this.app.all('/api/heatmap', function(req,res) {
// fill p here _.each(blabla, p.push(gethistory(someparams...)
var result = [];
function getHistory(params) {
var options = { ...};
var callback = function(error, response, body) {
if(error) { //wtv
} else {
// what to do with the body here ? return body ? result.push(body) ?
}
}
request(options, callback);
}
Q.all(p).then(function() {
});
}
So the problem here is that I when all of the request to be done , put everything in an array/object then send the whole thing to the client. How to have getHistory returning the fetched value (after the request is done ).
Hope it's clear.
The core problem here is that node.js-style callbacks and promises are not compatible. Promises emphasize on return values, node emphasizes on callbacks.
Therefore you need a sort of adapter that wraps node's callback convention properly, a process called Promisifcation. This can be done manually, but it's tedious at best and error-prone when you are not careful. Luckily, since node's conventions are well-established, it can be automated. Q has a few helpers for that, but Bluebird is quite a bit more convenient in this regard.
So the easy way to do it is to switch to Bluebird as the promise library and to use promisifyAll.
var Promise = require('bluebird');
var request = Promise.promisifyAll(require("request"));
this.app.all('/api/heatmap', function(req, res) {
var requests = blabla.map(function (item) {
return request.getAsync({ /* params */ });
});
Promise.all(requests).then(function (responses) {
res.send( JSON.stringify(responses) ); // whatever
}).catch(function (error) {
res.send( "An error ocurred: " + error ); // handle error
});
}
FWIW, here's another answer that shows how the same would look like when done properly with Q:
// promisified request
function requestAsync(options) {
var result = Q.defer();
request(options, function(error, response, body) {
if (error) {
result.reject(error);
} else {
result.resolve(body);
}
});
return result.promise;
}
// returns promises for heatmapVolumes
function getHistory(params) {
return requestAsync({
method: 'GET',
url: 'https://api.kaiko.com/v1/charts/' +
encodeURIComponent(params.exchange) + '/' +
encodeURIComponent(params.pair) +'/history',
qs: params.qs,
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
}
}).then(function (body) {
return heatmapVolume(body, params.exchange, params.pair);
}).catch(function (error) {
// log detailed error and send less revealing message downstream
console.error('error fetching trades', error);
throw new Error('Something went wrong while fetching trades');
});
}
// usage
this.app.all('/api/heatmap', function(req, res) {
getHistory({
exchange: "foo", pair: "bar", qs: "qux"
}).then(function (heatmap) {
res.send(200, heatmap);
}).catch(function (error) {
res.send(500, error);
});
});
Used Q.deferred and it worked as documented \o/
function getHistory(params) {
var deferred = Q.defer();
var options = {
method: 'GET',
url: 'https://api.kaiko.com/v1/charts/' + params.exchange + '/' + params.pair +'/history',
qs:qs,
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
}
}
var callback = function(error, response, body) {
if(error) {
console.log('error fetching trades', error);
res.send(500, 'Something went wrong while fetching trades');
} else {
var body = heatmapVolume(body, params.exchange, params.pair);
// console.log("result!", body.exchange, body.pair);
result.push(body);
// return body;
deferred.resolve();
}
}
request(options, callback);
return deferred.promise;
}
Here's the code:
app.get('/vklogin', function(request, response) {
console.log('Авторизация через соц.сеть "Вконтакте"'.green);
var url_parts = url.parse(request.url, true);
var query = url_parts.query;
var data = querystring.stringify({
client_id: '4836170',
client_secret: 'cPkR53zhon0lU7TAiz9f',
code: query.code,
redirect_uri: 'http://' + request.headers.host + '/vklogin'
});
var options = {
host: 'oauth.vk.com',
port: 443,
path: '/access_token?' + data,
method: 'GET'
};
var httpsreq = https.request(options, function(response) {
response.setEncoding('utf8');
response.on('data', function(chunk) {
var chunk = JSON.parse(chunk);
pg.connect(dbconfig, function(err, client, done) {
if (err) {
return console.error('Ошибка подключения к БД',err);
}
client.query('select * from users where vk = $1', [chunk.user_id], function(err, result) {
done();
if (err) {
console.error('Ошибка получения данных',err);
} else {
if (result.rows[0]) {
console.log(result.rows[0]);
request.session.authorized = true;
request.session.userid = result.rows[0].id;
} else {
console.log('Попытка создания нового пользователя. ');
client.query("insert into users (email, vk) values ('" + chunk.email + "', " + chunk.user_id + ") returning id", function(err, result) {
done();
if (err) {
console.error('Ошибка записи данных в БД', err);
} else {
request.session.authorized = true;
request.session.userid = result.rows[0].id;
console.log('Добавлен новый пользователь # ' + result.rows[0].id);
}
});
}
}
client.end();
});
console.log("№ пользователья: " + request.session.userid);
});
});
});
httpsreq.end();
if (request.session.authorized) {
response.writeHead(301, {
Location: 'http://' + request.headers.host + '/cabinet'
});
} else {
response.writeHead(301, {
Location: 'http://' + request.headers.host
});
}
response.end();
});
That is why outside functions session is not saved? What is wrong in my code?
Inside the function, everything is fine, outside functions - undefined.
After this session, the logic must be maintained and be available everywhere, too, everywhere, or is not it?
Tried to declare a variable with the session, but it also did not work, and no error does not give, do not even know where to dig.
var sess;
app.get('/vklogin', function(request, response) {
sess = request.session;
// other code...
});
UPD:
My problem is related to the lack of understanding of the control of asynchronous processes. I can not understand how to perform the originally one - database queries, information preservation in the session, and then check the session variables and forwarding to the desired page.
If you know how to make the correct execution order for me, write the answer.
Ok, I find need async pattern. Look here: http://book.mixu.net/node/ch7.html
I am trying to parse an object from a javascript (a blog post head and body) through a node.js server and on to save it in the mongoDB.
this is the parsing code:
function saveState( event ) {
var url = '';
var postTitle = headerField.innerHTML;
var article = contentField.innerHTML;
var post = {
title: postTitle,
article: article
};
var postID = document.querySelector('.save').getAttribute('id');
if(postID != "new"){
url += "?id=" + postID
}
var request = new XMLHttpRequest();
request.open("POST", "draft" + url, true);
request.setRequestHeader("Content-Type", "application/json");
request.send(post);
}
this is sent to this node server handler:
app.post('/draft', routes.saveDraft);
exports.saveDraft = function(req, res){
var id = url.parse(req.url, true).query.id;
var post = db.getPostByID(id);
if(id){
console.log('post id' + id);
db.savePost(id, req.body.head, req.body.article);
}
else{
db.newPost(req.body.head, req.body.article);
}
res.render('editDraft.hbs', post); //send the selected post
};
and then, sent to one of these DB functions:
exports.newPost = function (postTitle, article) {
new postCatalog({title:postTitle,
_id:1,
author:'temp',
AuthorID:2,
date:'2/3/12',
inShort:article.substring(0,100),
content:article ,
published:false
}).save(function (err, login) {
if (err) {
return console.log('error');
}
else {
console.log('Article saved');
}
});
}
exports.savePost = function (id, postTitle, article) {
postCatalog.find({_id: id}).save(function (err, login) {
if (err) {
return console.log('error');
}
else {
console.log('Draft saved');
}
});
}
now, I just can't get this to work..
I am new to node and I could really use your help!
thanks
EDITED:
the parameters being sent to the DB saving functions were not written properly.
but i'm still stuck in the same place, where the data is being sent but not saved correctly. I think there's something wrong with my getPostByID function but I can't figure it out:
exports.getPostByID =function (id) {
var post = postCatalog.find({_id: id}, function(err, post){
if(err) {
return handleError(err);
}
else{
if(post > 0){
post = post[0];
}
return post;
}
});
return post;
}
I am using express (including bodyparser) and mongoose. view engine is hbs.
thanks again.
You have to write it the asynchronous way, e.g. your getPostByID:
exports.getPostByID = function (id, callback) {
postCatalog.find({_id: id}, function(err, post) {
if (err) {
callback(err);
}
else if (post && post.length > 0) {
callback(null, post[0]);
}
else {
callback(null, null); // no record found
}
});
};
And this is true for your whole code. It's totally different and the way you tried it will never work under Node.js.
BTW there is a mongo-driver method findOne which is better suited in this special case but I didn't want to change your code too much.
I want to get use this rest api with authentication. I'm trying including header but not getting any response. it is throwing an output which it generally throw when there is no authentication. can anyone suggest me some solutions. below is my code
var http = require('http');
var optionsget = {
host : 'localhost', // here only the domain name
port : 1234,
path:'/api/rest/xyz',
headers: {
'Authorization': 'Basic ' + new Buffer('abc'+ ':' + '1234').toString('base64')
} ,
method : 'GET' // do GET
};
console.info('Options prepared:');
console.info(optionsget);
console.info('Do the GET call');
var reqGet = http.request(optionsget, function(res) {
console.log("statusCode: ", res.statusCode);
res.on('data', function(d) {
console.info('GET result:\n');
process.stdout.write(d);
console.info('\n\nCall completed');
});
});
reqGet.end();
reqGet.on('error', function(e) {
console.error(e);
});
The request module will make your life easier. It now includes a Basic Auth as an option so you don't have build the Header yourself.
var request = require('request')
var username = 'fooUsername'
var password = 'fooPassword'
var options = {
url: 'http://localhost:1234/api/res/xyz',
auth: {
user: username,
password: password
}
}
request(options, function (err, res, body) {
if (err) {
console.dir(err)
return
}
console.dir('headers', res.headers)
console.dir('status code', res.statusCode)
console.dir(body)
})
To install request execute npm install -S request
In your comment you ask, "Is there any way that the JSOn I'm getting in the command prompt will come in the UI either by javascript or by Jquery or by any means."
Hey, just return the body to your client:
exports.requestExample = function(req,res){
request(options, function (err, resp, body) {
if (err) {
console.dir(err)
return;
}
// parse method is optional
return res.send(200, JSON.parse(body));
});
};