nodejs why is variable persisting on page reloads? - node.js

I have the following express route:
const express = require('express');
const route = express.Router() ;
let a = 0;
route.get('/', (req,res)=> {
a++;
res.send(`hello api we are here! ${a}`)
})
module.exports = route;
When i log on to my localhost I get the message : hello api we are here! 1
then when i refresh: hello api we are here! 2
Every time i hit refresh the value of a increments. I always thought that when my browser connects to the server the entire server is reloaded and run from top. What am i missing?

Your variable a has a scope outside of the route.get. That a will increment and store for as long as your express server process is running.

Related

Using Firebase function as a proxy server

I built an app with Vuejs which is hosted on firebase, I recently added dynamic rendering with rendertron to improve SEO, I'm hosting the rendertron on Heroku. The rendertron client work well.
In order to send requests coming from bots like googlebot to rendertron and recieve a compiled HTML file, I used firebase function, it checks for the user agent, if it's a bot then it sends it to the rendertron link, if it's not, it fetches the app and resend result.
Here's the function code:
const functions = require('firebase-functions');
const express = require('express');
const fetch = require('node-fetch');
const url = require('url');
const app = express();
const appUrl = 'khbich.com';
const renderUrl = 'https://khbich-render.herokuapp.com/render';
function generateUrl(request){
return url.format({
protocol:request.protocol,
host:appUrl,
pathname:request.originalUrl
});
}
function detectBot(userAgent){
let bots = [
"googlebot",
"bingbot",
"facebookexternalhit",
"twitterbot",
"linkedinbot",
"facebot"
]
const agent = userAgent.toLowerCase()
for(let bot of bots){
if(agent.indexOf(bot)>-1){
console.log('bot-detected',bot,agent)
}
}
}
app.get('*', (req,res)=>{
let isBot = detectBot(req.headers['user-agent']);
if(isBot){
let botUrl= generateUrl(req);
fetch(`${renderUrl}/${botUrl}`)
.then(res => res.text())
.then(body=>{
res.set('Cache-Control','public','max-age=300','s-maxage=600')
res.set('Vary','User-Agent');
res.send(body.toString())
})
}
else{
fetch(`https://${appUrl}`)
.then(res=>res.text())
.then(body=>{
res.send(body.toString())
})
}
});
I used the function as an entry point for firebase hosting, so it's invoked whenever someone enters the app.
I checked on the firebase dashboard to see if it's working, and I noticed that it crashed for exceeding the number of requests per 100 second quota, I don't have much users when I checked, and the function invocations reached 370 calls in one minute.
I don't see why I had a large number of calls all at once, I'm thinking that maybe since I'm fetching the website if the user agent is not a bot, then the function is re-invoked causing an infinite loop of invocations, but I don't know if that's really why ?
If it's an infinite loop, how can I redirect users to their entered url without reinvoking the function ? will a redirect work ?

Angular how to show live value of json object

In angular , I am trying to display one json object to client html. Using below route on server side.
const express = require('express');
const jsonRoute = express.Router();
jsonRoute .route('/json').get(function (req, res) {
var JsonObj = { rank: 73 } ;
res.end(JSON.stringify(JsonObj));
});
setInterval(function(){
JsonObj.rank = parseInt(Math.random()*100);
}, 1000); // this interval may be anything.from ms to minutes.
module.exports = jsonRoute ;
this works on http://localhost:4000/json and displays ,
{"rank":73}
But does not show the values changed in setInterval. And same route i am using in a angular service (using http).
import { HttpClient } from '#angular/common/http';
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class getjsonService {
uri = "http://localhost:4000/json";
constructor(private http: HttpClient) { }
jsondata(){
return this.http.get(`${this.uri}`);
}
}
This value i am displaying in component html page. The problem is, it is not showing updated value of json. Please suggest how can i show live json value in angular. Please note, in realtime my json object going to be big in size , around 100 keys and value and i want to show live value for all key. And also change value interval may not be fix as one second. it may be in milliseconds as well.
Thanks
By default http will not persistent the connection. It's http protocol limitation not from angular.If you want to show the value in real time, you need to use web sockets.
There are lot of library out there will help with real time data connection. https://socket.io/ is very popular. check this out
Tutorial :https://alligator.io/angular/socket-io/
Your problem is a structural one with how RESTful architecture works. Your server only sends the new data to your angluar app when your app asks for it, not when your server detects a change. What you will need to do is either add a set timeout in your angular project that will call the server for the new data every few seconds.
setInterval(function(){ var JsonData = getJsonService.jsondata() }, 3000);
console.log(JsonData);
//This will refetch the data every 3 seconds you can lower this number to make it refresh more often
The other option is to rewrite your server to use web-sockets as Ravin pointed out.
In your node.js code, you are re-inializing the JsonObj variable every time the request is made. You should store the value as a global variable:
const express = require('express');
const jsonRoute = express.Router();
var JsonObj = { rank: 73 };
jsonRoute .route('/json').get(function (req, res) {
res.json(JsonObj);
});
setInterval(function(){
JsonObj.rank = parseInt(Math.random()*100);
}, 1000); // this interval may be anything.from ms to minutes.
module.exports = jsonRoute ;

How to manage a background (scraping) process with node.js

I have an Express app which extract data from plenty of websites. To do it, currently I have to run a task with a route (e.g. localhost/scrapdata) which get the data and store it on my pgsql db. This task is running infinitely.
I have other routes to get the data from my db.
Is it a good strategy to start my scraping task with a route? Or there is another strategies?
This doesn't need to be an Express app, but a simple Node.js script that gets fired off at a specified interval. What you are looking for is Cron.
If you want to keep your current Express app, then I recommend keeping it's current structure, but use something like node-schedule. So in another file, you could have something like:
// my-job.js
const schedule = require('node-schedule')
module.exports = schedule.scheduleJob('42 * * * *', () => {
console.log('The answer to life, the universe, and everything!')
})
Then in your main app.js, just import the file to start the job:
const express = require('express')
...
require('./my-job')
Then in another route like /shutdown, you could do:
const express = require('express')
const j = require('./my-job')
const router = express.Router()
router.get('/shutdown', () => {
j.cancel()
res.json({ message: 'Canceled.' })
})
This is just an idea, the above has not been tested.
Keep in mind though, scraping websites is a gray area. If they offer an API, then use that instead.

nodejs, how to using module.exports to set multiple parameters in express router

for example:
app.js:
var express = require('express');
var app = express();
var routesTemp=require('./routes/temp');
var routesTempExport=require('./routes/temp_export');
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'mustache');
app.engine('mustache', require('hogan-middleware').__express);
app.use('/',routesTemp);
app.use('/',routesTempExport);
module.exports = app;
/routes/temp.js:
var express = require('express');
var router = express.Router();
router.get('/temp',function(req, res, next){
//how to set a object,function...etc in there to module.exports
res.end();
});
// or there to set
module.exports = router;
/routes/temp_export.js:
var express = require('express');
var router = express.Router();
var getParameters = require('./temp');
router.get('/temp_export',function(req, res, next){
//and how to use getParameters to get multiple;
res.end()
});
module.exports = router;
I tried to change the module.exports format
for example:
module.exports = {router:router,obj1:obj1};
module.exports = [router,obj1];
module.exports = router,obj1;
But did not succeed, and will lead to router can not be identified
If you set this in your module:
// in myModule.js
module.exports = {router:router,obj1:obj1};
Then, you can access both variables upon import by:
const myModule = require('myModule.js');
console.log(myModule.router);
console.log(myModule.obj1);
You do have to make absolutely sure that the two exported properties are already set when you export. If they get set sometime in the future via some async operation, then you have no idea if they will be set properly when you want to use them.
In looking at the comments in your code some more, it appears that you're trying to use a value computed in a route in temp.js, in a separate route in temp_export.js. You basically can't do that directly because routes come from all sorts of users so you can't store state from one route in any sort of global on your server and expect some other route to access that state (well, you could, but it wouldn't work properly most of the time).
Instead, what you would typically do is store that state in some way that is clearly identified with a specific client. Then, when that particular client makes the next request and that route gets the request, that route handler can check the client-specific store to see if there is some state there. Note that creating this state in the first place violates some principles of the REST design so your first design idea should be how to avoid doing this at all. It would be better to put the state into the webpage returned from the first route and then when it makes the next web request, it can include that state in a query parameter.
The usual places you can store client-specific state are:
By putting it into the returned web page (so the client can pick up that state to send it with the next request - often as a query parameter or in a form post).
By putting the state into a cookie. The cookie values will then be available to the server upon the next request.
By storing it in a server-side session that is uniquely tied to that browser/user. The server-side session state for a particular user is also available to the server upon the next request.
Remember that the more stateless your server is the better (in general). So, if there is temporary state relevant to a particular user, you'd like that to be stored in the browser and presented back to the server with the next request, rather than having the server try to keep track of it in between requests.

nodejs + documentdb warm up error

I've been trying to add DocumentDb support to my node.js Express application like it's explained in here https://azure.microsoft.com/en-us/documentation/articles/documentdb-nodejs-application/
Now if a user makes a request immediately after application's start she's getting an error because I suppose my DocumentDb database isn't ready yet.
What I want here is to make my user wait as database initialization is getting done.. how can I achieve this or what is a general approach in node.js to handle initialization that takes a place during the application's start?
var express = require('express');
....
var DocumentDBClient = require('documentdb').DocumentClient;
var config = require('./config');
var StoryDao = require('./models/storyDao');
var Home = require('./controllers/home');
var docDbClient = new DocumentDBClient(config.endPoint, {
masterKey: config.authKey
});
var storyDao = new StoryDao(docDbClient, config.databaseId, config.storyCollectionId);
var home = new Home(storyDao);
storyDao.init();
....
app.listen(app.get('port'), function(){
console.log('app started on http://localhost:' + app.get('port') + '; press Ctrl-C to terminate.');
});
Standing up a DocumentDB account is a one-time setup and should only take <5 minutes. The easiest thing to do is wait for it to finish.
Otherwise, you could set up a mock DAO... which would be useful from a unit testing perspective :)

Resources