Nodejs Api call in background - node.js

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.

Related

Updating API response to GET request with node js express

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}
]);
});

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.

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)

Is it possible to clear node.js require cache using express.js?

I wrote node.js small app for myself which was using small js code to clear require cache before handling request:
let clearCache
if (process.env.NODE_ENV === 'development') {
const {cache} = require
clearCache = (except) => {
for (let key in cache)
if (!except.includes(key) && key.indexOf('/node_modules/') === -1)
delete cache[key]
}
} else {
clearCache = () => {}
}
const { createServer } = require('http')
const persistentPaths = ['/path/to/my/db/file']
createServer((req, res) => {
clearCache(persistentPaths)
require('./handleRequest')(req, res)
}).listen(3000, () => {})
I believe this is the most efficient way to working with app in development.
When I change code it applies immediately and this worked fine to me.
Now I would like to work with express.js, and it seems to impossible to use this approach.
Okay, seems like it is self-answering question and I need to use nodemon.
But I really don't want to use tool which will be watch all files and will relaunch whole server. I'm afraid that it would be much slower.
In my example you can see that I reload files except db file, so my app in development connects to database only once and keeps that connection. In case of using nodemon app need to load also all node_modules code, make new connection to db each time when code changed, maybe it will connect each time to postgres and to redis.
Question is: I can't be the one who doing it this way, are there solutions to reload modules for express.js?
Did it! Would be glad if you will share your thoughts on it.
Because I can google only nodemon and chokidar solutions, maybe I'm doing something wierd, here is my solution:
server.ts:
import express from 'express'
const app = express()
const port = 3000
app.listen(port, () =>
console.log(`Example app listening at http://localhost:${port}`)
)
if (process.env.NODE_ENV === 'development') {
const {cache} = require
const persistentFiles: string[] = []
const clearCache = (except: string[]) => {
for (let key in cache)
if (!except.includes(key) && key.indexOf('/node_modules/') === -1)
delete cache[key]
}
app.use((req, res, next) => {
clearCache(persistentFiles)
const {router} = require('config/routes')
router.handle(req, res, next)
})
} else {
const router = require('config/routes')
app.use(router.handle)
}
And routes.ts:
import {Router, Request, Response} from 'express'
export const router = Router()
router.get('/', function (req: Request, res: Response) {
res.send('Hello world')
})
Usage: NODE_ENV=development NODE_PATH=src node src/server.js
(my IDE compiles ts to js while editing and put it right there, if you are using VS code it might be more complicated, I guess)
You don't need to write your one util. You can use module like chokidar for so. You can specify directory and ignore the same time.
You can reload on callback the watch.
Sample from there wiki:: https://github.com/paulmillr/chokidar
const chokidar = require("chokidar");
// Full list of options. See below for descriptions.
// Do not use this example!
chokidar.watch("file", {
persistent: true,
ignored: "*.txt",
ignoreInitial: false,
followSymlinks: true,
cwd: ".",
disableGlobbing: false,
awaitWriteFinish: {
stabilityThreshold: 2000,
pollInterval: 100,
},
ignorePermissionErrors: false,
atomic: true, // or a custom 'atomicity delay', in milliseconds (default 100)
});
// One-liner for current directory
chokidar.watch(".").on("all", (event, path) => {
// Reload module here.
});

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.

Resources