Automatically restarting Heroku NodeJS app - node.js

I have a NodeJS app hosted on Heroku. Is there a way for me to automatically run the "heroku restart" command every hour?
I found this answer but it looks like it's for a Rails project: Restart my heroku application automatically

Yup, use the Heroku SDK for Node.js.
Something like:
heroku.apps('my-app').dynos().restartAll()
Should do the trick. All of the dyno documentation can be found here: https://github.com/heroku/node-heroku-client/blob/master/docs/dyno.md. You can run a scheduled script using the Heroku scheduler.
I must warn you though, this is most certainly not the best way to do whatever you are trying to do. If you provide more insight into your problem I'm sure we can suggest something more appropriate than restarting your dynos all the time.

Using the Heroku v3 API it is possible using the request node module
var token = 'youAPIKeyHere';
var appName = 'yourAppName here';
var dynoName = 'yourDynoHere';
var request = require('request');
request.delete(
{
url: 'https://api.heroku.com/apps/' + appName + '/dynos/',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/vnd.heroku+json; version=3',
'Authorization': 'Bearer ' + token
}
},
function(error, response, body) {
// Do stuff
}
);
There is also a node wrapper that provides a similar experience, but is poorly documented and requires an understanding of the v3 API anyway
var token = 'youAPIKeyHere';
var appName = 'yourAppName here';
var dynoName = 'yourDynoHere';
var Heroku = require('heroku-client');
var heroku = new Heroku({ token: token });
heroku .delete('/apps/' + appName + '/dynos/' + dynoName)
.then( x => console.log(x) );
I also found it useful to experiment in browser with this code
var token = 'youAPIKeyHere';
var appName = 'yourAppName here';
var dynoName = 'yourDynoHere';
var xhr = new XMLHttpRequest();
xhr.open(
'DELETE',
'https://api.heroku.com/apps/' + appName + '/dynos/' + dynoName
);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('Accept', 'application/vnd.heroku+json; version=3');
xhr.setRequestHeader('Authorization', 'Bearer ' + token);
xhr.onload = function() {
console.log(xhr.response);
};
xhr.send();
I personally did find using the delete method a bit concerning. Caution should be taken with using the delete method and the /apps/$$appName$$ endpoint alone will delete the app. This is from personal experience
For any of the above, if you omit the dyno name, you will restart all dynos under the app
The other question references a repo that is no longer supported

Related

What is the best way to make calls to my own API on a live node server?

I have made a web application with a simple API. The code for the front-end and and the API are both served from the same host. The front end consumes the API by making basic http requests. While developing, I have been making these requests within the front-end using port 3000 from the locally run server.
What is the best way to do this on a production server (An AWS EC2 instance)?
How do I easily generalize this in the development code so I don't have to change it from
axios.get("localhost:3000" + otherParams)
.then(response => {
//use the response to do things
});
})
to
axios.get("http://99.999.999.999:80" + otherParams)
.then(response => {
//use the response to do things
});
})
every time I push an update to the live server? Is this just something that web developers have to put up with? Sorry if this is a dumb question..
We definitely don't have to put up with changing our code like that every time! (Thank the coding gods)
So I think what you are after is environment variables
For example: You could setup an environment variable called SERVER_URL
Then when you are running locally that variable is localhost:3000 but when you deploy to amazon it can be set to http://99.999.999.999:80
in node you consume the variable like this
process.env.WHATEVER_YOUR_VARIABLE_NAME_IS
So in your case it would be
axios.get(process.env.SERVER_URL + otherParams)
a popular module to help create these variables is dotenv, which is worth looking at.
As a little bonus answer to help (and hopefully not confuse you too much), axios lets you create your own instance of axios so that you don't have to repeat yourself. Their example is
const instance = axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});
So you could do something like
const api = axios.create({
baseURL: process.env.SERVER_URL
});
then you can replace your axios calls with your new instance of axios (api)
Something like this.
api.get(otherParams)
Hope that makes some sense and gets you back on track!
You can create a config.js file:
var configs = {};
configs.appPort = 3000;
configs.host = '192.168.99.100';
module.exports = configs;
Importing the configure file:
var configs = require('./config');
Axios:
axios.get(configs.host + ":" + configs.appPort + "/" + otherParams)
.then(response => {
//use the response to do things
});
})
You can also create environment variables like this:
configs.isProduction = false;
configs.localHost = "localhost";
configs.productionHost = "192.168.99.100";
And then you can check in your app if it is production, use productionHost, otherwise, use localHost.

nodejs url parse returns an extra undefined object

I'm writing a node js script to pull variables from the url line.
var http = require('http');//http package
var url = require('url');// url package
var server = http.createServer(function(req, res){// creates server
res.writeHead(200, {'Content-Type': 'text/html'});
var q = url.parse(req.url, true).query//pulls apart the url
var temp = q.temp;
res.write(temp);
console.log(q.temp);
res.end();
}).listen(7474)
Whenever it's tested, the script returns an extra variable of some kind. If I feed it http://localhost:7474/?temp=29 I get:
29
undefined
inside of my console. For some of my other functions in this script it causes the whole system to crash. And failure as
The first argument must be one of type string or Buffer. Received type undefined
Why is that? And how do I remedy the situation?
You should use res.write() again followed by res.end(). The latter isn't supposed to return data.
Though it can supposedly return a string value in the first argument and then define the encoding format of the string as the second argument, but I've never tried that...
"If data is specified, it is equivalent to calling response.write(data, encoding) followed by response.end(callback)."
https://nodejs.org/api/http.html#http_response_end_data_encoding_callback
var http = require('http');//http package
var url = require('url');// url package
var list = ['0', '0', '1', '0'];
var server = http.createServer(function(req, res){// creates server
res.writeHead(200, {'Content-Type': 'text/html'});
var q = url.parse(req.url, true).query//pulls apart the url
var temp = q.temp;
res.write(temp);
console.log(q.temp);
res.write(list[2] ); //testing artifact displayes them on a browser
res.end();
}).listen(7474)
In you case there is really no reason to have both calls to .write() so I would just combine them...
res.write(JSON.stringify([ temp, list[2] ]));
Or
res.write(JSON.stringify({ temp, listItem:list[2] }));
EDIT:
After running this code myself you have a different issue here, which is giving you those confusing errors. Try this for now ...
var http = require('http');//http package
var url = require('url');// url package
var list = ['0', '0', '1', '0'];
var server = http.createServer(function(req, res){// creates server
if (req.url === '/favicon.ico') {
res.writeHead(204);
res.end();
} else {
res.writeHead(200, {'Content-Type': 'text/html'});
var q = url.parse(req.url, true).query//pulls apart the url
var temp = q.temp;
res.write(temp);
res.write(list[2]); //testing artifact displayes them on a browser
res.end();
}
}).listen(7474)
server.on('listening', function() {
console.log('listening on port 7474')
});
The browser was making a 'default' request to favicon.ico after the page initial page loaded to do just that-load the favicon for each web page. That request will crash your program because it doesn't include the query parameter `?temp=, which you aren't currently checking for.
The solution I provided is not workable solution for a production application, but this is clearly a little demo project, so it should do the job for your use case.
If you want to develop a larger, stable, production application you are going to have to reorganize things quite a bit. This could be a starting point if you don't want to use a framework...
https://adityasridhar.com/posts/how-to-use-nodejs-without-frameworks-and-external-libraries
I use the Express framework for all my node apps, which makes some of the default configuration faster and there are thousands of tutorials online to get you started...
https://medium.com/#purposenigeria/build-a-restful-api-with-node-js-and-express-js-d7e59c7a3dfb
I just Googled these tutorials as an example, but you should do your own research to find the most suitable.

nodejs app is not able to connect to REST api nodejs app

I have a nodejs REST api hosted on localhost and I have a nodejs app that is consuming it. This app too is running on localhost. Everything was working fine but after a restart the webapp just could not connect to the REST api anymore. I am running Windows 10.
I tested the REST api with postman and also with browser, it worked. There is no issue with the REST api.
Tried changing the port numbers - same result.
I ran wireshark to see the difference between when requesting from browser and from nodejs webapp. Below is the screenshot. First two lines are when the nodejs app made the request and the next two are from browser.
I am not able to understand what is wrong here. I tried with a standalone nodejs script, that too failed. Below is the script I used.
var request = require('request');
var u = "xxx";
var p = "xxx";
var auth = "Basic " + new Buffer(u + ":" + p).toString("base64");
var username = "qqqq";
var password = "eeee";
var options = {
url : 'http://localhost:4001/api/v1/transaction',
headers : {
"Authorization" : auth
},
};
console.log(options.url);
request.get(options, function(error,response,body){
//console.log(options);
//console.log(response);
console.log(response.statusCode);
console.log(body);
if (!error && response.statusCode == 200) {
var userObj = JSON.parse(body);
callback(userObj);
} else {
console.log("---- Error ----")
console.log(error);
}
});
I have found the problem and I am posting the answer in the hope that someone would find it useful.
My hint was from the wireshark. (screenshot in the question) All successful requests went to [::1] not localhost or 127.0.0.1. After the reboot of the windows 10 machine, the REST api nodejs app was actually no longer serving on the ip v4 localhost but was serving on ip v6 localhost. There was absolutely no issue with the code.
Instead of using localhost in the url in the consuming webapp, I changed it to [::1] and it started to work.
.....
.....
var options = {
//url : 'http://localhost:4001/api/v1/transaction',
// replaced localhost with [::1]
url : 'http://[::1]:4001/api/v1/transaction',
headers : {
"Authorization" : auth
},
};
.....
.....

Watson api using node.js

I am trying to use this node.js code to use watson api which is in ibm cloud bluemix in our ios app. Can anybody tell me what this code is doing and provide us an answer how to use the watson service from our app.
var express = require('express');
var https = require('https');
var url = require('url');
// setup middleware
var app = express();
app.use(express.errorHandler());
app.use(express.urlencoded()); // to support URL-encoded bodies
app.use(app.router);
app.use(express.static(__dirname + '/public')); //setup static public directory
app.set('view engine', 'jade');
app.set('views', __dirname + '/views'); //optional since express defaults to CWD/views
// There are many useful environment variables available in process.env.
// VCAP_APPLICATION contains useful information about a deployed application.
var appInfo = JSON.parse(process.env.VCAP_APPLICATION || "{}");
// TODO: Get application information and use it in your app.
// defaults for dev outside bluemix
var service_url = '<service_url>';
var service_username = '<service_username>';
var service_password = '<service_password>';
// VCAP_SERVICES contains all the credentials of services bound to
// this application. For details of its content, please refer to
// the document or sample of each service.
if (process.env.VCAP_SERVICES) {
console.log('Parsing VCAP_SERVICES');
var services = JSON.parse(process.env.VCAP_SERVICES);
//service name, check the VCAP_SERVICES in bluemix to get the name of the services you have
var service_name = 'question_and_answer';
if (services[service_name]) {
var svc = services[service_name][0].credentials;
service_url = svc.url;
service_username = svc.username;
service_password = svc.password;
} else {
console.log('The service '+service_name+' is not in the VCAP_SERVICES, did you forget to bind it?');
}
} else {
console.log('No VCAP_SERVICES found in ENV, using defaults for local development');
}
console.log('service_url = ' + service_url);
console.log('service_username = ' + service_username);
console.log('service_password = ' + new Array(service_password.length).join("X"));
var auth = "Basic " + new Buffer(service_username + ":" + service_password).toString("base64");
// render index page
app.get('/', function(req, res){
res.render('index');
});
// Handle the form POST containing the question to ask Watson and reply with the answer
app.post('/', function(req, res){
// Select healthcare as endpoint
var parts = url.parse(service_url +'/v1/question/healthcare');
// create the request options to POST our question to Watson
var options = { host: parts.hostname,
port: parts.port,
path: parts.pathname,
method: 'POST',
headers: {
'Content-Type' :'application/json',
'Accept':'application/json',
'X-synctimeout' : '30',
'Authorization' : auth }
};
// Create a request to POST to Watson
var watson_req = https.request(options, function(result) {
result.setEncoding('utf-8');
var response_string = '';
result.on('data', function(chunk) {
response_string += chunk;
});
result.on('end', function() {
var answers_pipeline = JSON.parse(response_string),
answers = answers_pipeline[0];
return res.render('index',{'questionText': req.body.questionText, 'answers': answers})
})
});
watson_req.on('error', function(e) {
return res.render('index', {'error': e.message})
});
// create the question to Watson
var questionData = {
'question': {
'evidenceRequest': {
'items': 5 // the number of anwers
},
'questionText': req.body.questionText // the question
}
};
// Set the POST body and send to Watson
watson_req.write(JSON.stringify(questionData));
watson_req.end();
});
// The IP address of the Cloud Foundry DEA (Droplet Execution Agent) that hosts this application:
var host = (process.env.VCAP_APP_HOST || 'localhost');
// The port on the DEA for communication with the application:
var port = (process.env.VCAP_APP_PORT || 3000);
// Start server
app.listen(port, host);
Majority of this code is required for Node and for executing in the BlueMix environment. VCAP_SERVICES is the Bluemix environment variable that you use to obtain credentials for a given service that you are interested in using. In this case the service_name is set to "question_and_answer" to access the Question and Answer platform service.
In your Bluemix environment, you should have a Question and Answer service instance and an application. When the application is bound to the Question and Answer service it creates a service binding. The service binding has the application credentials to access the service instance. In this case, VCAP_SERVICES contains the URL, username and password of the binding used to communicated and authenticate with the service instance.
From your iOS app you will need a couple of things. First, you need a service binding and you have to, for now, create that in Bluemix. Once you have the credentials in Bluemix then you may use those in your iOS app. Or you could host an application on Bluemix and let that handle communication with Watson.
Next, you need the capability to invoke the Question and Answer service which is a RESTful service. In the code above, the variable options contains the necessary information to POST a question to the Watson service. Note that the service_url obtained from the credentials is appended with an additional path information to use the Watson For Healthcare domain.
From there you can create your question. Variable questionData contains the question in the code above. The question.questionText property is set to the question that you want to ask Watson, like, "Should I take aspirin on a daily basis?".
Then you can POST the question to Watson, as the following snippet does:
watson_req.write(JSON.stringify(questionData));
Node is asynchronous, so the response is handled in the http.request(...). The answer is sent back to the requesting application in
result.on('end', function() {
var answers_pipeline = JSON.parse(response_string),
answers = answers_pipeline[0];
return res.render('index',{'questionText': req.body.questionText, 'answers': answers})
})
Rest of the code is node specific. But I've outlined the basics that you need in your iOS application.
All the code is to handle a HTTPS request to the Watson Question and Answer service.
If you want to use the service in your IOs app you will have to:
Modify the nodejs code and add REST API capabilities.
For example, you should have an endpoint to receive a questions and return answers:
app.get('/api/question', function(req, res){
// Call the service with a question and get an array with the answers
var answers = watson.question(req.query);
// Send the answers and the question to the client in json
res.json({answers: answers, question: req.query.question});
});
Start your app in bluemix and save the URL, it will be something like:
http://my-cool-app.mybluemix.net
Call your API at http://my-cool-app.mybluemix.net/api/question?question=Watson
Notes:
For the IOs app you can use AFNetworking.
You should also read move about the question and answer service documentation here, and read the about the service API here

How to track usage on a node.js RESTful service with Google Analytics?

I've written a RESTful node.js service as a backend for http://www.cross-copy.net and would like to not only track usage of the web-client but also other clients (like commandline or Apps) which use the service for inter-device copy/paste. Is it possible to embed the Google Analytics JavaScript API into a node.js application and do server-side tracking?
Since all of the answers are really old, I will mention a new npm package:
https://www.npmjs.com/package/universal-analytics
It's really great and incredible easy to use.
Install universal analytics
npm install universal-analytics --save
In your routes file, require the module. (Replace process.env.GA_ACCOUNT with string like 'UA-12345678-1')
// Init GA client
var ua = require('universal-analytics');
var visitor = ua(process.env.GA_ACCOUNT);
Now inside your endpoint functions, you can track a pageview. (Replace request.url with the current url string like '/api/users/1')
// Track pageview
visitor.pageview(request.url).send();
Read the documentation on UA for more info on this module.
As Brad rightfully sad, there was nothing for Node... So I wrote a nodejs module tailored for this these last few days and just published it on NPM: node-ga
The module is still really new (barely trying it in production on a pet project), so don't hesitate to give your input :)
You won't be able to just drop ga.js into your Node project. It has to be loaded in a browser to function correctly.
I don't believe there is anything out there for Node yet (correct me if I'm wrong!), but you should be able to easily adapt the existing PHP classes for doing logging server-side:
https://developers.google.com/analytics/devguides/collection/other/mobileWebsites
You can see how the URL to request the tracking GIF is constructed within ga.php. Translate ga.php to JS and you're set.
$utmGifLocation = "http://www.google-analytics.com/__utm.gif";
// Construct the gif hit url.
$utmUrl = $utmGifLocation . "?" .
"utmwv=" . VERSION .
"&utmn=" . getRandomNumber() .
"&utmhn=" . urlencode($domainName) .
"&utmr=" . urlencode($documentReferer) .
"&utmp=" . urlencode($documentPath) .
"&utmac=" . $account .
"&utmcc=__utma%3D999.999.999.999.999.1%3B" .
"&utmvid=" . $visitorId .
"&utmip=" . getIP($_SERVER["REMOTE_ADDR"]);
I tried out node-ga, but didn't get event tracking to work. nodealytics did the job.
See Core Reporting API Client Libraries & Sample Code (v3).
There is also the following version: Google APIs Client Library for Node.js (alpha).
I wrote a script to query data with Node.js from Googles Analytics Core Reporting API (v3). The script and a detailed setup description is available here.
Here is the script part:
'use strict';
var googleapi = require('googleapis');
var ApiKeyFile = require('mywebsiteGAapi-6116b1dg49a1.json');
var viewID = 'ga:123456700';
var google = getdefaultObj(googleapi);
var Key = getdefaultObj(ApiKeyFile);
function getdefaultObj(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var jwtClient = new google.default.auth.JWT(Key.default.client_email, null, Key.default.private_key, ['https://www.googleapis.com/auth/analytics.readonly'], null);
jwtClient.authorize(function (err, tokens) {
if (err) {
console.log(err);
return;
}
var analytics = google.default.analytics('v3');
queryData(analytics);
});
function queryData(analytics) {
analytics.data.ga.get({
'auth': jwtClient,
'ids': viewID,
'metrics': 'ga:users,ga:pageviews',
'start-date': 'yesterday',
'end-date': 'today',
}, function (err, response) {
if (err) {
console.log(err);
return;
}
console.log(JSON.stringify(response, null, 4));
});
}

Resources