Updating API response to GET request with node js express - node.js

I am a web development noob. I am building a web app in node js express, and I am able to populate a route with a dummy json. I don't want to use a database but just have live data being updated every few seconds.
app.route('/robot-data').get( async (req, res) => {
res.json([ // dummy data
{"data":0},
]);
});
My html is able to read from this API fine. The problem is, I would like to update the response, eventually with an emit event but for testing I am trying to just do it periodically.
let dataSource = 0;
const updateDataSource = () => {
const delta = Math.random();
dataSource += delta;
app.get('/robot-data', (req, res) => {
res.json([
// updated data
]);
});
}
const PORT = process.env.PORT || 8080;
app.listen(PORT, _ => {
setInterval(()=> updateDataSource(), 3000);
});
However, when I run this the json at the endpoint doesn't change when I refresh. Basically, I want to have what is happening at this api for my json. https://api.wheretheiss.at/v1/satellites/25544
I've looked into websockets etc but I really just want to do what the ISS api is doing.

Your code executes the statement app.get('/robot-data', (req, res) => {...}) repeatedly, which does not work. Middleware functions like (req, res) => {...} must be set up only once, and if their behaviour shall change over time, they can refer to variables defined outside of them, and these variables can change.
In your case, this would look so:
let dataSource = 0;
const updateDataSource = () => {
const delta = Math.random();
dataSource += delta;
};
app.get('/robot-data', (req, res) => {
res.json([
{"data":dataSource}
]);
});

Related

Why is my Heroku Express API data persistent even though it's only coming from a variable

I created a simple API using express, and deployed it to Heroku, this is the code for it:
const express = require("express");
const app = express();
const cors = require("cors");
app.use(express.json());
app.use(cors());
app.use(express.static("build"));
let notes = [
{
id: 1,
content: "HTML is easy",
date: "2022-05-30T17:30:31.098Z",
important: true,
},
{
id: 2,
content: "Browser can execute only Javascript",
date: "2022-05-30T18:39:34.091Z",
important: false,
},
{
id: 3,
content: "GET and POST are the most important methods of HTTP protocol",
date: "2022-05-30T19:20:14.298Z",
important: true,
},
];
const generateId = (arr) => {
const maxId = arr.length < 0 ? 0 : Math.max(...arr.map((item) => item.id));
return maxId + 1;
};
app.get("/", (req, res) => {
res.send(`<h1>Hello World!</h1>`);
});
app.get("/api/notes", (req, res) => {
res.json(notes);
});
app.get("/api/notes/:id", (req, res) => {
const id = Number(req.params.id);
const note = notes.find((note) => note.id === id);
if (note) {
res.json(note);
} else {
res.status(404).end();
}
});
app.delete("/api/notes/:id", (req, res) => {
const { id } = Number(req.params);
notes = notes.filter((note) => note.id !== id);
res.status(204).end();
});
app.post("/api/notes", (req, res) => {
const body = req.body;
if (!body.content) {
return res.status(400).json({
error: "Content Missing",
});
}
const note = {
content: body.content,
important: body.important || false,
date: new Date(),
id: generateId(notes),
};
notes = notes.concat(note);
res.json(note);
});
app.put("/api/notes/:id", (req, res) => {
const newNote = req.body;
notes = notes.map((note) => (note.id !== newNote.id ? note : newNote));
res.json(newNote);
});
const PORT = process.env.PORT || 3001;
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
as you can see, the data served to the frontend (A React app) comes from the '/api/notes' endpoint, this endpoint returns a response with the notes array.
After deploying to Heroku (https://fierce-chamber-07494.herokuapp.com/) the functionality of adding notes, and setting importance all work perfectly normal, but what I wasn't expecting was for the data to be persistent even after refreshing the page, visiting it in another device, etc. The data only comes from a variable, not a database, nothing. So why is it persistent? does Heroku modify the variable itself? how does this work?
The top-level code of an Express server often runs once, when you start up the server. Variables declared at that top level are then persistent if there are any handlers that reference them.
Consider how a client-side page with JavaScript works - the page loads, and then the JavaScript runs. If you keep the tab open for a couple hours and then come back to it, you'll see that variables declared on pageload still exist when you come back. The same sort of thing is happening here, except that the persistent environment is on your server, rather than on a client's page.
The code that starts up the Express server - that is, your
const express = require("express");
const app = express();
const cors = require("cors");
app.use(express.json());
app.use(cors());
...
and everything below it - doesn't run every time a request is made to the server. Rather, it runs once, when the server starts up, and then when requests are made, request handlers get called - such as the callback inside
app.get("/", (req, res) => {
res.send(`<h1>Hello World!</h1>`);
});
So, the variables declared at the top-level are persistent (even across different requests) because that server environment is persistent.
That said - something to keep in mind with Heroku is that with their free and cheap tiers, if no request is made to your app for a period of time (maybe 30 minutes), Heroku will essentially turn your server off by spinning down the dyno until another request is made, at which point they'll start your server up again, which will run the top-level code again. So while you'll sometimes see a top-level variable that appears to have its mutated values persist over multiple requests, that's not something to count on if your Heroku plan doesn't guarantee 100% uptime for your server.

why localhost:3000/?age=12 works but localhost:3000/?age="12" doesn't in Express Node JS

When working on middleware's in Node JS, i have implemented a code on if age>=18 user can access the website otherwise not but when i type query like localhost:3000/?age=12 it works but localhost:3000/?age="12" it doesn't works anyone knew why it happens and how to resolve it
code:
const express = require('express')
const app = express()
const port = 3000
// middleware
// req,res we need to modify so it is there
// next is a function it will proceed when route is called
const reqFilter=(req,res,next)=>{
console.log('reqFilter');
// we have to call next otherwise it will keep loading in browaer
// eg:-> if age is older then 18 user can access page
if(req.query.age<18){
res.send('Please Confirm You are over 18')
}
else if(!req.query.age){
res.send("please put down age")
}
else{
next();
}
}
// using the middleware
app.use(reqFilter)
app.get('/', (req, res) => {
res.send("welcome to homepage")
})
app.get('/users', (req, res) => {
res.send("welcome to users page")
})
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
From express docs: https://expressjs.com/en/api.html.
As req.query’s shape is based on user-controlled input, all properties and values in this object are untrusted and should be validated before trusting. For example, req.query.foo.toString() may fail in multiple ways, for example, foo may not be there or may not be a string, and toString may not be a function and instead of a string or other user-input.
You're comparing the query parameter to the Number 18, You need to check the type of the query. You can do so with if (typeof req.query.age == 'string') and perhaps convert the "18" to a Number.
You can do so by adding + like that: +req.query.age

How do I make server-side fetch calls?

I have a React web application which currently does fetch calls client-side to update a dashboard with live information (let's say current weather, as an example), meaning that with an increase in users it will cause unnecessary traffic calls and could potentially crash this weather website.
What I am trying to understand is how can I make those fetch calls be server-side? I have looked into creating a Node.js Express server, but I am unsure if it has the functionality to make fetch calls to a remote host.
Here is my code with request-weather which does not really work, unfortunately.
const { response } = require('express');
const express = require('express');
const app = express();
var fetch = require('node-fetch');
const port = process.env.PORT || 5000;
app.use(express.json());
// This displays message that the server running and listening to specified port
app.listen(port, () => console.log(`Listening on port ${port}`));
// create a GET route
app.get('/request-info', (req, res) => {
res.send({ information: 'information call successful' });
});
app.get('/request-weather', (req, res) => {
fetch('http://thisotherwebsite.com/weather-query-that-returns-json',
{method: 'GET',
headers: {' Accept': 'application/json'}})
.then(res => {
return res;
})
});
Couple things:
Your /request-weather handler makes the request to thisotherwebsite but doesn't do anything with the response.
Your .then(res => { return res; }) doesn't actually do anything. You're just taking what fetch already returns and returning it.
If you want to send the response back to the browser you might do something like this:
fetch(...) // make the request
.then(result => result.json()) // extract the data
.then(data => {
res.json(data); // send it to the browser
})
If you want to do additional processing you could await the fetch call and then do whatever else you need to do with it:
app.get('/request-weather', async (req, res) => { // make handler async
// get data from the other site
const data = await fetch(...)
.then(response => response.json());
// package it up with some other stuff
responseData = {
fromOtherSite: data,
myExpressStuff: {
foo: 1,
bar: 2,
}
}
// return it to the browser
res.json(responseData);
Reference:
fetch: response.json() - Extracting data from a fetch response
express response.json() - Sending json to the response (usually to the browser)

How to save external API response to Firebase

Im working with a React App where I present a list top Podcasts. I'm using iTunes Search API to dynamically present data to the user. For now, I working with a Node Express server to setup my custom endpoints. The problem is that the API has a request limit, so I tought that I could save what I get from the response to Firebase and present the data from firebase instead.
To my question;
Can in some way save the response I get from iTunes Search API to Firebase?
For now my code for fetching data from my API Endpoints looks like this in my Node+Express server:
const express = require('express');
const unirest = require('unirest');
const app = express();
const port = process.env.PORT || 5000;
// Get all Episodes from a specific podcast
app.get('/api/podcast/episodes', (req, res) => {
const feedurl = req.query.feedurl
unirest.get(feedurl)
.end((response) => {
res.status(200).send(response.body)
});
});
// Get Podcast by ID
app.get('/api/podcast/:id', (req, res) => {
const podID = req.params.id;
unirest.get(`https://itunes.apple.com/lookup?id=${podID}&country=se`)
.end((response) => {
res.status(200).send(response.body)
});
});
// Get Podcast Categorys
app.get('/api/podcast/:category/:amount', (req, res) => {
const categoryID = req.params.category;
const amount = req.params.amount;
unirest.get(`https://itunes.apple.com/se/rss/toppodcasts/limit=${amount}/genre=${categoryID}/explicit=true/json`)
.end((response) => {
res.status(200).send(response.body)
});
});
// Get Podcast Categorys
app.get('/api/categorys', (req, res) => {
unirest.get('https://itunes.apple.com/WebObjects/MZStoreServices.woa/ws/genres?id=26&cc=se')
.end((response) => {
res.status(200).send(response.body)
});
});
app.listen(port, () => console.log(`Listening on port ${port}`));
Im just looking for someone who could point me in the right direction how to proceed. Cause for now I'm stuck, big time.
Depending on how long you want to cache the response, you can use a whole different things - a physical database like MySql, Sqlite, MongoDB etc to locally persist data.
If you only want to keep the cached result for a short period of time, you can use in-memory cache or just any other tool that offers you same functionality. Redis is also a good contender as a temporary store, especially when you expect to scale to more than one node instance for your application.
Below, I have modified a part of your code to cache result for 10mins, using memory-cache npm module
const express = require('express');
const unirest = require('unirest');
const cache = require('memory-cache');
const CACHE_DURATION = 10 * 60 * 1000; //10mins
const app = express();
const port = process.env.PORT || 5000;
// Get all Episodes from a specific podcast
app.get('/api/podcast/episodes', (req, res) => {
const cacheKey = req.query.feedurl; //Or anything unique to this route
const cachedData = cache.get(cacheKey);
if(cachedData) {
return res.json(cachedData);
}
const feedurl = req.query.feedurl
unirest.get(feedurl)
.end((response) => {
res.status(200).send(response.body);
cache.put(cacheKey, response.body, CACHE_DURATION);
});
});
---- the rest of your code ----
You can hit the route as many times as you want and be guaranteed that data will be fetched from iTunes only once in 10mins.
The second and subsequent requests will be served a lot faster from cache.
Let me know if this is what you are looking for.

Nodejs Api call in background

I'm developing an app with node JS, the app generates a report calling the endpoint api.example.com/generate-report
But this report takes around 1 minute on be generated, then I want to implement something like this:
User click on generate report
System return response {generating:"ok"}
After the system generate the report send a notification (this I what I know how to do)
User get the report
Is this possible with nodejs?
After I do some research, this can be easily done using Promises.
To run the following code it's necessary to install express and node uuid
npm install --save express
npm install --save uuid
node index.js
The source code of index is:
//index.js
const express = require("express");
const app = express();
const PORT = process.env.PORT || 5000;
const uuidV1 = require('uuid/v1');
// this is where we'll store the results of our jobs by uuid once they're done
const JOBS = {};
app.get("/", (req, res) => {
res.send("It works!");
});
app.get("/startjob", (req, res) => {
let times = [100, 1000, 10000, 20000];
let promises = [];
for (let time of times) {
promises.push(new Promise((resolve, reject) => {
setTimeout(resolve, time, `${time} is done.`);
}));
}
// obviously, you'd want to generate a real uuid here to avoid collisions
let uuid = uuidV1();
console.log(uuid);
Promise.all(promises).then(values => { JOBS[uuid] = values; });
res.redirect(`progress/${uuid}`);
});
app.get("/progress/:uuid", (req, res) => {
if (JOBS[req.params.uuid] === undefined) {
res.send("Still processing your request.");
} else {
res.send(`Here's your result: ${JOBS[req.params.uuid]}.`);
// instead of immediately deleting the result of the job (and making it impossible for the user
// to fetch it a second time if they e.g. accidentally cancel the download), it would be better
// to run a periodic cleanup task on `JOBS`
delete JOBS[req.params.uuid];
}
});
app.listen(PORT, () => {
console.log(`Listening on localhost:${PORT}.`);
});
When the code runs you will be redirected to /process/uuid and I get the status of the process.
This needs some improvements because I want the response like "{process:uuid}" and I can store this on my Local Storage to use after.
Well, I hope this help to someone.

Resources