I have a route in my express app, which is supposed to do the following:
Get some data from outside (OK)
Show a HTML page with socket.io listening for messages (OK)
Perform some calculations, which take a long time
Send a message trough socket.io after each one one completed (OK)
When all calculations are completed, show a result page (problem)
So, a simplified version of my code is:
module.exports = function(io) { // This is so I can use socket.io inside the route
var express = require('express');
var router = express.Router();
[... and other required]
router.post('/', function(req, res, next) {
res.render('loading'); // This renders the template which holds accepts and renders socket.io messages
pefromCalculaton();
sendSocketIOMessage();
session.data = resultData; // I get the result of all calculations and put that in the session. It's a quick fix, don't judge, I've got no presistancy in my project as for now...
res.redirect('results'); // And here I'd like to go to the other route, which will display the data, getting it from the session.
});
return router;
}
Since this doesn't work, I am probably trying to do something really stupid here. But what I actually want to do is:
Perform calculations
While performing the calculations, update progress using sockets
When calculation is done, render a template, showing all the results.
Well, my friend, as you know that one can't send two responses from within one request. So here is what you need to do.
module.exports = function(io) { // This is so I can use socket.io inside the route
var express = require('express');
var router = express.Router();
[... and other required]
router.post('/', function(req, res, next) {
var taskId = (new Date()).getTime(); //timestamp to get unique task id.
res.render('loading');
startCalculations(function(progressIndicator){ //progress callback
io.emit('progress',progressIndicator);
},function(result){ // Finish callback
session[taskId] = result;
io.emit('finish',{ taskid: taskId });
});
});
router.get('/result:taskId', function(req, res, next) {
var result = session[req.params.taskId];
if(!result)
{
//Result already consumed!
res.render('expired');
return;
}
delete session[req.params.taskId];
res.render('result', {result: result});
});
//progress callback will be called when we want to report progress
//done callback indicates our results are ready.
function startCalculations(progress, done){
//This is just a stupid piece of code, just to emulate loading.
//Your awesome async code will replace this
//In your case it will not be a loop and there will be a callback
//To signal finish.
var result = 0;
for(i = 0; i < 100; i++)
{
result = result + i;
//Simulate progress
progress(i);
}
//Simulate finish -- result has 0 to 99 all added up ;)
done(result);
}
return router;
}
Now on the html front you can have ...
this is how your loading view would look like.
<script src="/socket.io/socket.io.js"></script>
<script src="http://code.jquery.com/jquery-1.11.1.js"></script>
<script>
var socket = io();
//Init code.
socket.on('progress', function(progressIndicator){
//Your jquery mojo to show progress
displayProgress(progressIndicator);
});
socket.on('finish', function(task){
//Redirect to result page from frontend (you were trying to do
//on backend -- node.js)
window.location.href = "/result/" + task.taskId;
//OR
//window.location.replace("/result/" + task.taskId);
});
</script>
Hope this makes sense and helps ...
Let me know if you need anything else.
Have fun!
Node is asynchronous. Use callbacks or promises to make sure that the result page is shown only when the calculations has been completed.
Related
I'm working on a SPA website with node.js, jQuery, mongoose and MongoDB for a shopping website.
The ajax requests and responses work perfectly when starting from the index.html file. So for example begining on http://localhost:3000 and someone clicks on a link called 'products' I send an ajax request to the the server and the server sends the necessary product information back asynchronously which lead to http://localhost:3000/products. But the problem is that if someone types http://localhost:3000/products directly in the search bar it will show the json representation of the products.
This is my code:
script.js
function redirect (link) {
$.ajax({
type: 'GET',
url: 'http://localhost:3000/' + link,
contentType: 'application/json',
data: {
link
},
success: function (res) {
let container = $('#contentToSwap');
container.html('');
res.products.forEach(function (products_) {
...
});
}
});
}
app.js
var Product = require('./models/product');
var mongoose = require('mongoose');
var bodyParser = require('body-parser');
var path = require('path');
var express = require('express');
var app = express();
mongoose.connect('mongodb://localhost:27017/shopping');
var PORT = process.env.PORT || 3000;
app.use(express.static(path.join(__dirname, 'public')));
app.use(bodyParser.json());
app.get('*', function(req, res) {
Product.find(function(err, docs) {
let productChunks = [];
let chunksize = 4;
let display = [];
for (var i = 0; i < docs.length; i++) {
if (docs[i].productType == req.query.link) display.push(docs[i]);
}
for (var i = 0; i < display.length; i += chunksize) {
productChunks.push(display.slice(i, i + chunksize));
}
res.send({ products: productChunks });
});
});
app.listen(PORT, function () {
console.log('Listening on port ' + PORT);
});
So I need some sort of frontend routing if the user doesn't start at the index.html file. I know that I could write my own router to route the urls correctly and that I could route all requests back to the index.html like
app.get('*', function(req, res) {
res.sendFile(__dirname + '/public/index.html');
});
But then I cannot load all the necessary product information from the server when someone clicks a link. So I'm a little bit confused on hwo to tackle this issue. Any help is appreciated
This is usually achieved by separating api routes from normal ones by adding specific url prefixes such as /api for all routes that return json data. What you can do is to specify /api/whatever-you-want, make it the target for your ajax call and place it above app.get('*' ....
Since routes and middleware functions are resolved top to bottom, it will be matched by your ajax call only, leaving the /products unaffected.
answer to question -- Is it possible to redirect user from /api/products to /product if the request wasn't generated by ajax?
Yes, it is possible by adding request query parameter to ajax call which will not be present on normal call and then check those on the server side and decided what to do if it (that specific query parameter) is missing or not.
Let's assume some client side JS that generates ajax call.
fetch('/api/products?api=true')
.then((data) => data.json())
.then((json) => console.log(json));
Notice the request url - /api/products?api=true
Now assume a normal call from html file.
products
These two calls differ in that api query parameter (ajax call has it, the other one doesn't).
And for the server side part of the task -- request query parameters object can be accessed via query property on request object (req.query).
app.get('/api/products', (req, res) => {
if (!req.query.api) {
// if get request doesn't contain api param. then
// handle it accordingly, e.g. do redirect
return res.redirect('/products');
}
// request comming from ajax call, send JSON data back
res.json({ randomStuff: 'abcd' });
});
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);
}
I have been creating a website with Mean stack and I stuck at some point. I have a mongo db database and I am currently getting each file from database (to show them on Main page) with my Rest Api which is build with Express.
Server.js
var express = require('express');
var app = express();
var mongojs = require('mongojs');
var db = mongojs('mongodb://username...', ['myApp']);
var bodyParser = require('body-parser');
app.use(express.static(__dirname + '/public'));
app.use(bodyParser.json());
app.get('/myApp', function (req, res) {
db.myApp.find(function (err, docs) {
console.log(docs);
res.json(docs);
});
});
app.get('/myApp/:id', function (req, res) {
var id = req.params.id;
console.log(id);
db.myApp.findOne({_id: mongojs.ObjectId(id)}, function (err, doc) {
res.json(doc);
})
});
app.listen(3001);
console.log('Server running on port 3001');
There is 2 get method and I can understand that because they have different parameters. So when I call them from controllers, there is no problem because if I provide id, it will call the second get method. But for example I want to use something like this in my website;
app.get('/myApp', function (req, res) {
db.myApp.find({}).limit(2).skip(0, function(err, docs) {
console.log(docs);
res.json(docs);
});
});
This get method have no parameter like the first get method in server.js but they do different jobs. This is limiting my search with 2 file. How can I use different get methods like this in my Mean Stack application?
This is my code for calling get method from my main controller. How can I make sure to call specific get method? Thanks..
$http.get('/myApp').success(function(response) { .. });
What you want is not possible. Somehow you need to distinguish between your 2 intentions, either by giving the endpoints different names (like you already suggest in your comment) or by providing for example a query parameter so you could do a call like:
$http.get('/myApp?limit=2').success(function(response) { .. });
When limit is omitted, you could return all results.
Something like:
app.get('/myApp', function (req, res) {
var limit = req.query.limit;
if (limit === undefined) {
// Return everything
} else {
// make sure limit is some valid number
// ... and do a mongo query limited to this number
}
});
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.
I have the following code
index: function (req, res) {
var Request = unirest.get("https://poker.p.mashape.com/index.php?players=4").headers({ "X-Mashape-Authorization": "xxxxxxxxxxxxxxxxx" }).end(function (response) {
players = response.body;
showdown_total = players.showdown.length;
showdown = Array();
});
console.log(players);
// Send a JSON response
res.view({
hello: 'world',
//players: players
});
},
It works great if I add the res.view inside unirest get, but I want to send those variables to the view and be able to add another unirest request
Thanks for your help
That is how asynchronous code works in Node.js.
Basically, when an operation doesn't evaluate ASAP, node doesn't wait for it. It just says, "fine, no worries, just tell me when you are done".. sort of.
The thing is, in your code, you don't tell node when your get request. is done. You just fire away the view to the client before the request function even starts thinking about fetching the data.
How to make node wait ?
You have some options. Either, give it a callback function (do this when you are done), or you have to nest your functions. Those two are kind of the same thing really.
I'll show you one solution, nested functions:
var urlOne = "https://poker.p.mashape.com/index.php?players=4",
urlTwo = "http://some.other.url",
headers = { "X-Mashape-Authorization": "xxxxxxxxxxxxxxxxx" };
// Run first request
unirest.get(urlOne).headers(headers).end(function (response) {
players = response.body;
showdown_total = players.showdown.length;
showdown = Array();
// Run second request
unirest.get(urlTwo).headers(headers).end(function (response) {
someVar = response.body;
// Show all my data to the client
res.view({
players: players,
someOther: someVar
});
});
});
Other solutions:
If you don't want to nest the functions, give them a callback to run when they are done.
Use a module for handling asynchronous code, for example one of the more popular ones called Async.
I would suggest you to read more about callbacks, asynchronous code and nodejs before jumping directly on the external libraries.
There is another way....you could use fibers!
Read some docs here!
var sync = require('synchronize');
index: function (req, res) {
sync.fiber(function(){
var response = sync.await(
unirest.get("https://poker.p.mashape.com/index.php?players=4").headers(
{ "X-Mashape-Authorization": "xxxxxxxxxxxxxxxxx" }
).end(sync.defer())
);
var players = response.body;
console.log(players);
// Send a JSON response
res.view({
hello: 'world',
players: players
});
});
}