I have a page that when you access it, it runs tests. I want my tests to run every week so Im trying to create a cron job that access my express route every week. Sort of like Im making a get request.
For testing sake I have a cron job that runs every 2 minutes:
//schedule job every 2 minutes
schedule.scheduleJob("*/2 * * * *", function () {
console.log('inside cron function')
});
router.get('/my_page_route_name', ensureAuthenticated, async function(req, res){
res.render('my_page_route_file', {
layout: 'dashboard.handlebars',
jsMain: 'my_page_route_js',
});
});
If I go in my url to http://localhost:1337/my_page_route_name It goes inside the router.get request just fine. But Is there a way I can trigger my cron job to call the same route and render the page every 2 minutes?
I'm unsure of how to do this because the router.get function uses res.render, and I have no res variable in my cron job
{{ EDIT }}
My cron jobs works and triggers a POST request to my route:
schedule.scheduleJob("*/10 * * * *", async function() {
console.log('inside cron function');
const resp = await fetch("http://localhost:1337/my_page_route_name/", {
"headers": {
"content-type": "application/json"
},
"method": "post",
"body": JSON.stringify({
"username":"exvar",
"password":"examplevar2"
})
});
});
and i created an express route to receive the POST request;
router.post('/my_page_route_name', async function(req, res){
res.render('my_page_route_name_html', {
layout: 'dashboard.handlebars',
jsMain: 'my_page_route_name_jsmain',
});
})
If I make a request in postman I can see the posr route returns the webpage html, but no scripts have been run, for example I have <script> document.querySelector('.breadcrumbs').append('[[ html loadded ]]') </script> inside my html file that gets loaded, but the code doesnt seem to be ran in the response I recieve
Use a fetch package in node as http requests get pretty complicated quickly.
const fetch = require('node-fetch');
//schedule job every 2 minutes
schedule.scheduleJob("*/2 * * * *", async function() {
const response = await fetch('https://yourdomain.tld/my_page_route_name');
const body = await response.json();
console.log('inside cron function', body);
});
router.get('/my_page_route_name', ensureAuthenticated, async function(req, res){
res.render('my_page_route_file', {
layout: 'dashboard.handlebars',
jsMain: 'my_page_route_js',
});
});
Related
I have a to-do list app that updates a string in a mongodb database with every change in state of the to-do list - that string is parsed on reload to render the state. It works great, except when I trigger 5 or 6 state changes quickly in sequence, it hangs the page. As example, if I delete 5 tasks over the course of a couple seconds. I assume the problem is handling all those post requests, but maybe it's on the updating mongodb side? Is there a way to handle a lot of post request like that in a some sort of queue?
Client side:
function sendData(obj) {
fetch('/jsondata', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(obj),
}).catch(function (error) {
console.log(error);
});
console.log('db updated');
}
Here's the mongo side that runs when the POST request is requested from client...if it helps:
app.post('/jsondata', function (req, res) {
updateUserCache(currentUserEmail, JSON.stringify(req.body));
});
async function updateUserCache(email, newState) {
const foundUser = await user.findOne({
email: email,
});
foundUser.cachedState = newState;
const newDate = await Date.now();
foundUser.date = newDate;
await foundUser.save();
console.log('user cache has been updated');
}
It's hanging because you're never sending back a response from your backend code, and at some point the browser will stop making new connections to it.
So make sure you end the requests properly:
app.post('/jsondata', async function (req, res) {
await updateUserCache(currentUserEmail, JSON.stringify(req.body));
res.end();
});
I have as an assignment to do a nodeJS HTTP server with some form of metrics on it.
For this assignment, I am not allowed to use any form of external libraries & as the metric of the request I'll store the response time.
For route handling ill use a simple switch statement comparing the URL of the request
const http = require("http");
var metrics = []
http.createServer((req, res) => {
switch(req.url){
case "/route"
var start = Date.now();
// some code to process the request, maybe some requests to the database, maybe retrieve some files etc
res.end();
metrics.push(Date.now() - start);
break;
}
}).listen(8080,()=>console.log("Server running!"))
The thing is, if I want to do this app with one or a low number of requests this method would be ok,
however, in any other normal scenarios, this would be awful for any further changes to those metrics & much more.
I'm thinking of trying to solve this with some sort of event listeners that I would call at the beginning & the end of my request.
Something that would store information about the request at the beginning & launch an event at the end to stop processing the metrics.
server.on('end', (data)=>{
//end metrics somehow
})
Although it seems a little hard to implement, especially as I don't really want to overwrite a nodeJS event to add some data on it ( for instance I may want to add an id of the request, or a timestamp )
Is there a way to properly do this with nodeJS HTTP?
You can handle it by finish event of res object. The event will be called when you call res.end() (or something like that).
My recommendation, to metrics an API service, you will need more information than response times.
const http = require("http");
var metrics = []
http.createServer((req, res) => {
const startPoint = Date.now();
res.on("finish", () => {
if (req.url === "/metrics") {
return; // no metrics for metrics route
}
metrics.push({
path: req.url,
method: req.method,
status: res.statusCode,
dateTime: startPoint,
times: Date.now() - startPoint,
});
});
switch (req.url) {
case "/route":
// some code to process the request, maybe some requests to the database, maybe retrieve some files etc
setTimeout(() => {
res.end();
}, 1000);
break;
case "/metrics":
res.setHeader('Content-Type', 'application/json');
res.write(JSON.stringify(metrics));
res.end()
break;
default:
res.statusCode = 404;
res.end();
break;
}
}).listen(8080, () => console.log("Server running!"))
As you can see, when you call GET http://localhost:8080/metrics api, the response will look like this:
[
{
"path": "/test",
"method": "GET",
"status": 404,
"dateTime": 1613631702906,
"times": 1
},
{
"path": "/route",
"method": "GET",
"status": 200,
"dateTime": 1613631702906,
"times": 1004
}
]
create your own middleware on each routes, if you can't use framework like express or koa
Note: this is the simplest example, but it gives a clue of research
class Middleware {
use(func) {
this.next_func = (
(stack) =>
(next) =>
stack(
func.bind(this, next.bind(this)),
)
)(this.next_func);
}
next_func = (next) => next();
}
I would like to have custome response time-out, so that I am trying to send the response from setTimeout callback if the processing time exceeds certain interval.
In the following example, I set 20ms as time-out period within which services.getReport() has to be processed otherwise the API has to send response as response_timed_out.
The following code works fine but it throws exception like
Cannot set headers after they are sent to the client
router.post("/getReport", (req, res) => {
setTimeout(() => {
res.send({ status:503, msg: "response_timed_out" });
}, 20);
services.getReport(req, res);
});
You can use setTimeout method on the request object to define timeout time for each route.
router.post("/getReport", (req, res) => {
req.setTimeout(2000);
services.getReport(req, res);
});
You can also define timeout time globally for all routes on the server.
const server = app.listen(port, () => {});
server.setTimeout(15000);
You can also provide the second argument which is callback that will run when timeout occurs. If you want the callback then you call setTimeout on the response.
router.post("/getReport", (req, res) => {
res.setTimeout(2000, () => res.send('timed_out'));
services.getReport(req, res);
});
I have the following snippet:
const express = require('express')
const app = express()
const cronJob = require('cron').CronJob
app.get('/test', (req, res) => {
// Do something here
}
new cronJob('* * * * * *', () => {
// Call localhost:3000/test here
}, null, true, 'Asia/Manila')
app.listen(3000, () => console.log('Successfully listened to app 3000'))
Usually on node, localhost:3000/test runs if this is called on the browser right? I wanted to make the CRON run this without typing it on the browser once the node app starts. If possible also regardless of the hostname, whether it's localhost or not, the CRON should make the request without being typed on the browser. Can this be done?
I read the comments above on the questions itself and decided to add my thoughts even thought it seems like you got a solution.
In my opinion it will be much more clean for you to call the "method" itself instead of hitting "http" for getting the response you need.
You have 2 options:
Hitting the "domain.com/test" endpoint with a request call.
Simply calling the same method the above url is doing.
In this way, you will "save" the overhead of need to "set-up" a new request to the express app with response and request headers. (example below)
let's say this is your code:
const handleTestData = () => return 'something';
app.get('/test', (req, res) => {
const result = handleTestData();
res.send(result);
}
new cronJob('* * * * * *', () => {
// instead of calling http and getting the response
// and adding overhead, just call the function
const result = handleTestData();
// do what you need with the result
}, null, true, 'Asia/Manila')
I'm looking for an easy solution to front-end and back-end communication.
I want to write simple JS front-end client where a user can put a number between 1 an 10 000 to guess the number that server has generated.
So the client job is to send number that user is guessing. The server should test if secretNumber is higher or lower then that provided by the user and it should send back that info.
For now, my server only sends that secret number. I'm getting it inside my client console, so the connection is working.
My question is how should I modify my server code to read the number value from request, test it and then send the right response (example -> your number is higher than the secretNumber)?
This is my server:
const express = require('express');
const app = express();
const cors = require('cors');
app.use(cors());
app.use((request, response, next) => {
console.log(request.headers);
next();
});
app.use((request, response, next) => {
request.secretNumber = Math.floor(Math.random() * 10000) + 1;
next();
});
app.get('/', (request, response) => {
response.json({
secretNumber: request.secretNumber
});
});
app.listen(3001, () => console.log("Listening on 3001"));
Here is my front-end JS code (I'm using axios):
export function guessNumber(guessValue) {
return dispatch => {
dispatch({ type: GUESS_NUMBER });
axios
.post('/guess', {
isNumber: guessValue,
})
.then(response => {
console.log(response);
})
.catch(error => {
console.log(error);
});
};
}
And I was here looking for answer, but maybe I'm to inexperiened and I need some real example...
First you need to persist the secretNumber between requests. At the moment you are generating a new value on each request.
Assuming just one client using the backend concurrently, you can do this by generating the secretNumber when the server starts and keep it in memory (assign it to a variable).
Then you can simply use route params to capture the client's guess:
app.get('/guess/:guess', (request, response) => {
const guess = params.guess;
// compare guess with secretNumber and respond accordingly
});
Alternatively you can use the request's body (https://expressjs.com/en/4x/api.html#req.body) instead of route params.