NodeJS restify API caching best practice - javascript

I am very new to NodeJS and I am building my first API using restify.
I want to find out what is best practice for caching the response data - each API call must have its own cache time.
I have looked at res.cache() but that seems to be only per user request and not a global application cache.
I then looked at restify-cache but the documentation did not clearly tell me how to use it.
My application works like this:
server.js code:
var restify = require('restify');
var mysqlDB = require('./config/connection');
// REST server declaration and configuration
var server = restify.createServer({
name: 'test-api',
version: '0.0.1'
});
server.pre(restify.pre.sanitizePath());
server.use(restify.queryParser());
server.use(restify.acceptParser(server.acceptable));
server.use(restify.queryParser());
server.use(restify.bodyParser());
server.listen(9007, function() {
console.log('%s listening at %', server.name, server.url);
mysqlDB.handleDisconnect();
console.log(new Date() +': Started Cricket API on port 9007');
});
var routes = require('./routes')(server);
routes.js code:
module.exports = function(app) {
app.get('/', function(req, res, next) {
return res.send("You have reached the test API");
});
var fixtures = require('./controllers/fixtures');
app.get('/getfixtures', fixtures.getFixtures); // Get All Fixtures
};
fixtures.js code snippet:
this.getFixtures = function (req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
console.log("Get All Fixtures");
var mysql = mysqlDB.getConnection();
var query = "SELECT * FROM fixtures WHERE fixture_date >= CURDATE() ORDER BY fixture_date, fixture_time";
mysql.query(query,function(err,rows){
if(err) {
var status = mysqlDB.getErrorStatus(err.code);
return res.status(status.code).send("Error : "+ status.Message);
} else {
var data = [];
for (i in rows){
var item = rows[i];
var output = util.formatDate(item.fixture_date);
item.fixture_date = output;
data.push(item);
};
return res.send(data);
}
});
};
Can someone please send me in the right direction? I don't know where to add the caching part?

From the library file:
server.use(cache.before); is a middleware that will be triggered to load before the request is handled, going to Redis and checking if the header_{url} key and payload_{url} exits, and at that case the value is returned.
You could put it as mentioned in this gist:
https://gist.github.com/jeffstieler/3d84fa5468c7eadb7685
var server = restify.createServer({
name: 'test-api',
version: '0.0.1'
});
server.pre(restify.pre.sanitizePath());
server.use(cache.before);
server.use(restify.queryParser());
server.use(restify.acceptParser(server.acceptable));
server.use(restify.queryParser());
server.use(restify.bodyParser());
server.on('after', cache.after);
In your code I would add the cache.before after you sanitize the path as this will be saved in Redis. also a next() should be included in every route cached.

I ended up using node-cache
It was easy to use since I come from a Java/Play Framework background - hopefully it helps someone else in future.
Example usage:
var nodeCache = require( "node-cache" );
var myCache = new nodeCache();
var cachedValue = myCache.get("alltests", true);
if (cachedValue != undefined) {
return res.send(cachedValue);
} else {
// Do work here and then:
success = myCache.set("alltests", valueHere, cacheTime);
}

Related

How to query in nodejs?

this is my first time asking questions and this is basically my last resort in finding some answers. Im a noob and beginner in javascript so please use simple terms with me.
So i have an issue. I dont know how to query.
- As in do i put all my queries in one script or do i have to split them up to different scripts.
- Right now, i have a server.js and i put all my codes in there. including queries. So how do i run just one of them.
- and also if there is such a thing for me to query for just another number like 4. Do i have to go back to the script to manually change from '110' to '4' or can i just enter it somewhere.
Some examples are:
//length of 110
db.collection.find({length: "110"}, function(err, collection) {
if( err || !collection) console.log("No collections with 110 in length");
else collection.forEach( function(length) {
console.log(length);
} );
});
//shows record of length 110 and length 340
var length = ['110', '340']
length = length.join('|');
var re = new RegExp(length, 'i')
db.collection.find({length:{$regex: re}}, function(err, collection) {
if( err || !collection ) console.log("User not found");
else collection.forEach (function(length){
console.log(length);
});
});
How do i query to only run for one of them in mongodb. Appericiate the help alot guys
You already know that node.js is used for making servers. So there are 2 ways you can query the database. Since you're new to node, I'm going to list both the ways :
1.A http GET for querying data (Standard Way)
You can write a simple http server and set a route for getting the type of data you want.I've written a small file so that you can understand:
var express = require('express');
var http = require('http');
var app = express();
var bodyParser = require('body-parser');
// Db settings
var mongodb = require('mongodb');
var MongoClient = mongodb.MongoClient;
var url = 'mongodb://localhost:27017/my_database_name';
function getConnection(url, callback) {
MongoClient.connect(url, function(dbErr, dbRes) {
if(dbErr) {
return callback(err, null);
}
callback(null, dbRes);
});
}
// Configure app to use bodyparser
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
var port = process.env.PORT || 7013;
app.set('port', port);
app.use(function(req, res, next) {
console.log(req.ip+ ":"+port + req.url + " "+req.method);
next();
});
app.get('/getCityData', function(req, res, next) {
var cityName = req.query.city;
getConnection(url, function(conErr, db) {
if(conErr) {
return res.send("ERROR IN GETTING connection");
}
var cityCollection = db.collection('cities');
cityCollection.find({"city":cityName}).toArray(function(err, result) {
if(err) return res.send("error in querying data");
db.close();
res.send(result);
});
});
});
var httpServer = http.createServer(app).listen(port, function() {
console.log("Express server listening on port "+app.get('port'));
});
You can query the server using curl or postman like this :
2.Using command line arguments :
You can also do it the easy way using command line arguments by passing the parameter to query, but it's less flexible:
var mongodb = require('mongodb');
var MongoClient = mongodb.MongoClient;
var url = 'mongodb://localhost:27017/my_database_name';
function getConnection(url, callback) {
MongoClient.connect(url, function(dbErr, dbRes) {
if(dbErr) {
return callback(err, null);
}
callback(null, dbRes);
});
}
var cityName = process.argv[2]; // that's the argument you'd receive
getConnection(url, function(conErr, db) {
if(conErr) {
return res.send("ERROR IN GETTING connection");
}
var cityCollection = db.collection('cities');
cityCollection.find({"city":cityName}).toArray(function(err, result) {
if(err) return res.send("error in querying data");
db.close();
console.log(result);
});
});
Pass that commandline argument like this while executing file :
I hope it helps

Better way to run different db queries based on path in node.js and express

I'd like to have some advice on how to call different database queries based on the path in the url using express.js. Here's a working example code but I'm not sure if that is the good way of doing it:
server.js
var express = require('express'),
app = express(),
Promise = require("bluebird"),
db = require('./db/managedb'); // database modules for sequelize.js
app.get('/p/:section/:optional?', function(req, res){
var section = req.params["section"];
var optional = req.params["optional"];
if(section == "index"){
Promise.props({
main: db.db.query('CALL sp1()'),
second: db.db.query("CALL sp2()")
}).then(function(obj){
res.render(section+'.html',obj)
}).catch(function (error) {
})
}else if(section == "overviews"){
var page = req.query.page || 0;
Promise.resolve(db.db.query('CALL sp3(:page)',{page:page})).then(function(d){
res.render(section+'.html',d)
})
}else if(section == "reviews"){
var page = req.query.page || 0;
var review_id = req.query.review_id || 1;
Promise.resolve(db.db.query('CALL sp4(:review_id,:page)',{review_id:review_id,page:page})).then(function(d){
res.render(section+'.html',d)
})
}
})
Is it an okay solution? My concern is that if I kept adding more conditions for new sections, it might be quite messy, but is there a better way to call different database queries based on the path? Any suggestion is appreciated.
I would simply use different routing handlers, like this:
var express = require('express'),
app = express(),
Promise = require('bluebird'),
db = require('./db/managedb'); // database modules for sequelize.js
app.get('/p/index/:optional?', function(req, res) {
Promise.props({
main: db.db.query('CALL sp1()'),
second: db.db.query('CALL sp2()')
}).then(function(obj) {
res.render('index.html', obj);
}).catch(function(error) {
});
});
app.get('/p/overviews/:optional?', function(req, res) {
var page = req.query.page || 0;
Promise.resolve(db.db.query('CALL sp3(:page)', {page: page})).then(function(d) {
res.render('overviews.html', d);
});
});
app.get('/p/reviews/:optional?', function(req, res) {
var page = req.query.page || 0;
var review_id = req.query.review_id || 1;
Promise.resolve(db.db.query('CALL sp4(:review_id,:page)', {review_id: review_id, page: page})).then(function(d) {
res.render('reviews.html', d);
});
});
If you have more shared code between the routes, you could use multiple routing handlers using next() callbacks. For example, you could write your content from d somewhere into req and then just call next() instead of res.render(…). You then add another routing handler with the same signature as your old one (matching all routes) below those three and call res.render(section + '.html', req.d) (or other code) in there.

Node.js respond with asynchronous data

Recently I started learning a little bit about Node.js and it's capabilities and tried to use it for some web services.
I wanted to create a web service which will serve as a proxy for web requests.
I wanted my service to work that way:
User will access my service -> http://myproxyservice.com/api/getuserinfo/tom
My service will perform request to -> http://targetsite.com/user?name=tom
Responded data would get reflected to the user.
To implement it I used the following code:
app.js:
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
var proxy = require('./proxy_query.js')
function makeProxyApiRequest(name) {
return proxy.getUserData(name, parseProxyApiRequest);
}
function parseProxyApiRequest(data) {
returned_data = JSON.parse(data);
if (returned_data.error) {
console.log('An eror has occoured. details: ' + JSON.stringify(returned_data));
returned_data = '';
}
return JSON.stringify(returned_data);
}
app.post('/api/getuserinfo/tom', function(request, response) {
makeProxyApiRequest('tom', response);
//response.end(result);
});
var port = 7331;
proxy_query.js:
var https = require('https');
var callback = undefined;
var options = {
host: 'targetsite.com',
port: 443,
method: 'GET',
};
function resultHandlerCallback(result) {
var buffer = '';
result.setEncoding('utf8');
result.on('data', function(chunk){
buffer += chunk;
});
result.on('end', function(){
if (callback) {
callback(buffer);
}
});
}
exports.getUserData = function(name, user_callback) {
callback = user_callback
options['path'] = user + '?name=' + name;
var request = https.get(options, resultHandlerCallback);
request.on('error', function(e){
console.log('error from proxy_query:getUserData: ' + e.message)
});
request.end();
}
app.listen(port);
I wish I didn't screwed this code because I replaced some stuff to fit my example.
Anyway, the problem is that I want to post the response to the user when the HTTP request is done and I cant find how to do so because I use express and express uses asynchronous calls and so do the http request.
I know that if I want to do so, I should pass the makeProxyApiRequest the response object so he would be able to pass it to the callback but it is not possible because of asyn problems.
any suggestions?
help will be appreciated.
As you're using your functions to process requests inside your route handling, it's better to write them as express middleware functions, taking the specific request/response pair, and making use of express's next cascade model:
function makeProxyApiRequest(req, res, next) {
var name = parseProxyApiRequest(req.name);
res.locals.userdata = proxy.getUserData(name);
next();
}
function parseProxyApiRequest(req, res, next) {
try {
// remember that JSON.parse will throw if it fails!
data = JSON.parse(res.locals.userdata);
if (data .error) {
next('An eror has occoured. details: ' + JSON.stringify(data));
}
res.locals.proxyData = data;
next();
}
catch (e) { next("could not parse user data JSON."); }
}
app.post('/api/getuserinfo/tom',
makeProxyApiRequest,
parseProxyApiRequest,
function(req, res) {
// res.write or res.json or res.render or
// something, with this specific request's
// data that we stored in res.locals.proxyData
}
);
Even better would be to move those middleware functions into their own file now, so you can simply do:
var middleware = require("./lib/proxy_middleware");
app.post('/api/getuserinfo/tom',
middleware.makeProxyApiRequest,
middleware.parseProxyApiRequest,
function(req, res) {
// res.write or res.json or res.render or
// something, with this specific request's
// data that we stored in res.locals.proxyData
}
);
And keep your app.js as small as possible. Note that the client's browser will simply wait for a response by express, which happens once res.write, res.json or res.render etc is used. Until then the connection is simply kept open between the browser and the server, so if your middleware calls take a long time, that's fine - the browser will happily wait a long time for a response to get sent back, and will be doing other things in the mean time.
Now, in order to get the name, we can use express's parameter construct:
app.param("name", function(req, res, next, value) {
req.params.name = value;
// do something if we need to here, like verify it's a legal name, etc.
// for instance:
var isvalidname = validator.checkValidName(name);
if(!isvalidname) { return next("Username not valid"); }
next();
});
...
app.post("/api/getuserinfo/:name", ..., ..., ...);
Using this system, the :name part of any route will be treated based on the name parameter we defined using app.param. Note that we don't need to define this more than once: we can do the following and it'll all just work:
app.post("/api/getuserinfo/:name", ..., ..., ...);
app.post("/register/:name", ..., ..., ... );
app.get("/api/account/:name", ..., ..., ... );
and for every route with :name, the code for the "name" parameter handler will kick in.
As for the proxy_query.js file, rewriting this to a proper module is probably safer than using individual exports:
// let's not do more work than we need: http://npmjs.org/package/request
// is way easier than rolling our own URL fetcher. In Node.js the idea is
// to write as little as possible, relying on npmjs.org to find you all
// the components that you need to glue together. If you're writing more
// than just the glue, you're *probably* doing more than you need to.
var request = require("request");
module.exports = {
getURL: function(name, url, callback) {
request.get(url, function(err, result) {
if(err) return callback(err);
// do whatever processing you need to do to result:
var processedResult = ....
callback(false, processedResult);
});
}
};
and then we can use that as proxy = require("./lib/proxy_query"); in the middleware we need to actually do the URL data fetching.

HTTP Proxy using node connect

Note: My question may be very similar to this question, but the solution is not working for me.
My problem is the same as the original poster. I need to access an external resource, and I need to proxy to it to get around cross domain security restrictions. I had also referenced this sample blog post: http://nthloop.com/blog/local-dev-with-nodejs-proxy/
The proxy is working to load external resources (anything containing 'cgi' in the url). But with this code, I am no logner able to hit local (static) files with the connect module. Times out, no error messages, etc...
I am posting the full code of my server.js file:
var httpProxy = require('http-proxy');
var connect = require('connect');
var endpoint = {
host: '11.0.0.120',
port: 8081
};
var proxy = new httpProxy.RoutingProxy();
var app = connect()
.use(function(req, res) {
if (req.url.indexOf('cgi') >= 0) {
proxy.proxyRequest(req, res, endpoint);
} else {connect.static(__dirname)};
})
.use(connect.static(__dirname))
.listen(8182);
This question's solution seemed to state that I needed to include an else condition. It doesn't wok without or without one.
Thanks for any help!
You can use next() to let request flow to the next middleware -
var app = connect()
.use(function(req, res, next) {
if (req.url.indexOf('cgi') >= 0) {
proxy.proxyRequest(req, res, endpoint);
} else {
next();
)};
})
.use(connect.static(__dirname))
.listen(8182);
Oops, got it working just by swapping the use(connect...) function to the first call. Don't know why this worked or if it is the best approach. Also, didn't need the else statement.
If someone can give a better explanation I can accept their answer. Here's my working code:
var httpProxy = require('http-proxy');
var connect = require('connect');
var endpoint = {
host: '11.0.0.120',
port: 8081
};
var proxy = new httpProxy.RoutingProxy();
var app = connect()
.use(connect.static(__dirname))
.use(function(req, res) {
if (req.url.indexOf('cgi') >= 0) {
proxy.proxyRequest(req, res, endpoint);
}
})
.listen(8182);

Node.JS run Sandbox in REST service

I am using Node Restify Module to create a REST service that accepts POST. Within the service I am trying to create a Sandboxed process using Node Sandbox module because we will be running dynamically inserted Javascript and if something goes wrong, I dont want it to affect the main Node instance.
When I try to create the Sandbox, something goes wrong and causes the REST service to come back empty.
Here is my code
var restify = require('restify');
var Sandbox = require("sandbox");
var logic;
function createSandbox(body) {
var s = new Sandbox();
s.run("1 + 1", function(output) {
logic = body.names + " has " + output.result;
});
}
function respond(req, res, next) {
createSandbox(req.body);
res.send(logic);
}
var server = restify.createServer();
server.use(restify.bodyParser({
mapParams: false
}));
server.post('/hello/:name', respond);
server.head('/hello/:name', respond);
server.listen(8080, function() {
console.log('%s listening at %s', server.name, server.url);
});
In my http request I have {"names":"rob"} in the body
I am expecting the following response
rob has 2
------------UPDATE-------------------
This works
var restify = require('restify');
var Sandbox = require("sandbox");
var logic;
function respond(req, res, next) {
var s = new Sandbox();
s.run("1 + 1", function(output) {
logic = req.body.names + " has " + output.result;
res.send(logic);
});
}
var server = restify.createServer();
server.use(restify.bodyParser({
mapParams: false
}));
server.post('/run/:id', respond);
server.head('/run/:id', respond);
server.listen(8080, function() {
console.log('%s listening at %s', server.name, server.url);
});
Sandbox.run() is asyncronous. It just sets the sandbox up to run at a later time and returns immediately before the code it sandboxes is actually run, so you're reading logic before it's set.
A quick demo;
var Sandbox = require("sandbox");
function createSandbox() {
var s = new Sandbox();
s.run("1 + 1", function(output) {
console.log("inside");
});
}
createSandbox();
console.log("outside");
> outside
> inside

Categories