Is It possible to call request with setInterval? - node.js

I'm new to node.js
I'm trying to call API with header every x seconds in node.js , And what i achieve is like
type some information of header for getting external API from client and post it to my backend server.
get data from External data which keeps changing data any second ,
call external API from backend server every seconds and send it to client.
get data from my backend server to client, and the data keep changing.
I'm not sure if the way I'm doing is okay .
, So, i tried to do like this:
In Node.js
app.post("/realtime", (req, res) => {
var url = req.body.GET_API_URL;
var header1_key = req.body.Headers1_key;
var header1_value = req.body.Headers1_Value;
var header2_key = req.body.Headers2_key;
var header2_value = req.body.Headers2_Value;
var header3_key = req.body.Headers3_key;
var header3_value = req.body.Headers3_Value;
var option = {
url: url,
method: "GET",
headers: {
[header1_key]: header1_value,
[header2_key]: header2_value,
[header3_key]: header3_value,
},
};
const interval = () => {
request(option, (error, response, body) => {
try {
res.json(JSON.parse(body));
} catch (error) {}
});
};
setInterval(interval, 1000);
});
in client
function getAPI() {
axios
.post("http://localhost:5000/realtime", {
GET_API_URL: convert.GET_API_URL,
Headers1_key: convert.Headers1_key,
Headers1_Value: convert.Headers1_Value,
Headers2_key: convert.Headers2_key,
Headers2_Value: convert.Headers2_Value,
Headers3_key: convert.Headers3_key,
Headers3_Value: convert.Headers3_Value,
})
.then(response => {
setRandom(response.data);
console.log(response);
})
.catch(error => {
console.log(error);
});
}
it doesn't get any error and it's didn't work as i expected.
So i wonder if what i do is right way to do or is complete wrong .
And if It's wrong I'd like to get advice .
if there is alternative to achieve it, i'd really appreciate it.
Thank you in advance

You can use Promise in javascript
function intervalAPI = async () => {
return new Promise((done, reject) => {
setIntrrval(() => {
try {
conse { data } = await axios(...);
done(data);
} catch (e) {
reject();
}
}, 1000);
}
}
Promise Docs

Related

How to return success on a Post API call to MongoDB in NodeJS

I'm new to fetching and posting data using an API, and I can't work out how to do something once my Post has been completed.
I have a function that calls the API with the Post data. I need to set the loading state to false once the Post has been completed. Everything works apart from that, the data gets sent to Mongo, I just need to turn off my loading spinner once it has completed.
How do I do this, please?
This is how I'm trying to do it:
const postData = async () => {
setLoading(true)
await axios.post('/api/addData',form)
.then(response => {
setLoading(false)
})
}
And this is the API bit:
import { connectToDatabase } from "util/mongodb"
export default async (req, res) => {
const { db } = await connectToDatabase()
await db
.collection("posts")
.insertOne(req.body);
}
There is two potential problem in your code, first you're not sending any data back to the front in your backend code. Usually you send back the id of the inserted element (It can be usefull to do some mutation in your front), you'll also need to try catch your call to the db to notify that something went wrong to the front end side :
import { connectToDatabase } from "util/mongodb"
export default async (req, res) => {
try {
const { db } = await connectToDatabase()
const insertedPost = await db
.collection("posts")
.insertOne(req.body);
res.status(201).send(insertedPost.insertedId);
// again it's up to you to know what can be usefull to your front-end to use
// Look at http status code online to know what's the best fit
} catch (err) {
res.status(500).send(err.message);
// send whatever that can be usefull for your front end to handle the error
}
}
In your front-end code you're using await with .then, it's weird usage. You can put your setLoading(false) after the await without the .then but you'll still need to try catch it. What I prefer to do is using the finally block to stop loading, so if my api call fail the loading is still stopped :
const postData = async () => {
setLoading(true)
try {
const response = await axios.post('/api/addData',form)
// do something with response
} catch (err) {
// notify user that something went wrong
} finally {
setLoading(false);
}
}
const postData = () => {
setLoading(true)
axios.post('/api/addData',form)
.then(response => {
// do something with response
})
.catch((err) => {
// notify user that something went wrong
})
.finally(() => {
setLoading(false);
})
}

How to use async await when the function in await needs to be looped dynamically

Here is my scenario.
I'm using NodeJS to make API requests.
I need to make an API request to fetch a set of data. I receive a limited set of records with a field called counter, which will let me know if I need to make another request to fetch the remaining data (pagination basically).
So I need to make multiple requests to the same API to fetch the total data.
Here is what I have tried. The resolve function is not returning data.
What should be the right syntax to achieve this?
app.post('/api/fetchData', async function(req, res) {
try {
var totalData = await getDataFromLoop(token, '');
} catch (e) {
console.log('Exception', e);
}
});
var loopArray = [];
function getDataFromLoop(accessToken, counter){
request({
url: 'API URL',
auth: {
'bearer': accessToken
}
}, function(err, response) {
if(err)
{
}
else
{
var res_data = JSON.parse(response.body);
if(res_data.hasOwnProperty('hasMore'))
{
loopArray.push(res_data.data);
console.log('One More Loop to go', res_data.offset);
getDataFromLoop(accessToken, res_data.offset);
}
else
{
console.log('Looping Done');
loopArray.push(res_data.data);
return new Promise(function(resolve, reject) {
resolve(loopArray);
});
}
}
});
}
You need to move the Promise outside of the request function call in getDataFromLoop
function getDataFromLoop(accessToken, counter){
return new Promise((resolve, reject) => {
request(...,
...
if (res_data.hasOwnProperty('hasMore')) {
...
getDataFromLoop(accessToken, res_data.offset).then(resolve)
} else {
...
resolve(loopArray)
}
)
})
}
Since you're using async...await, I guess you can achieve that with a simple while loop, like so:
let shouldFetchAgain = true;
let finalData = [];
while (shouldFetchAgain) {
const data = await fetch('<some_url>');
shouldFetchAgain = data.currentCount < data.total;
finalData.push(data.content);
}
// Process `finalData` here.
The code snippet above demonstrates how you can leverage while loop to decide whether you need to fetch the data again. This looks like a polling technique you're using to fetch paginated data.
Alternatively, you can check out PollingObserver.
Hope this helps you.

Axios.all, how to configure axios wait time to mitigate hung up?

My application uses an internal webservice for fetching data, i have a job which creates approx 500 requests which getsfired async to complete the fetch operation.
I make use of Axios, by creating an array of axios promises and then resolving them using using Axios.all();
It works fine until some 200 requests but post that i get socket hung up, however on the server side i see the requests are being processed.
How to configure axios to set custom time out, or is it a better idea to splice my promises array and then run them as multiple batches ?
Source code
let getAxiosPromiseArray = (urlList) => {
var axiosArrayofPromise = [];
return new Promise ( (resolve, reject) => {
try {
urlList.forEach ( (URL) => {
axiosArrayofPromise.push(axios.get(URL));
});
resolve(axiosArrayofPromise);
}
catch (err) {
reject("There is a problem getting Axios array of promises " + err);
}
})
}
async function processAxiosPromises (PromiseArray) {
try {
var results = []
results = await axios.all(PromiseArray);
return results;
}
catch(err) {
throw("There was a problem resolving promises array (Axios) " + err);
}
}
getallID().then ( (urlList) => {
return getAxiosPromiseArray(urlList);
}).then( (AxiosPromises) => {
return processAxiosPromises(AxiosPromises);
}).then ((resultData) => {
console.log(resultData);
});
Error
There was a problem resolving promises array (Axios) Error: socket hang up
First, that pair of functions getAxiosPromiseArray() and processAxiosPromises() needs fixing.
Your new Promise() construction is unnecessary. You can simply return Promise.all(arrayofPromise) (or axios.all(...) if you must) and do away with the other function.
Renaming the remaining function to something meaningful, you would end up with eg :
let getData = (urlList) => {
return Promise.all(urlList.map(URL => axios.get(URL)))
.catch(error => {
error.message = "There is a problem getting Axios array of promises " + error.message; // augment the error message ...
throw error; // ... and re-throw the errror.
});
};
And call as follows :
getallID().then(getData)
.then(resultData => {
console.log(resultData);
}).catch(error => {
console.error(error);
});
That will put you on solid ground but, on its own, is unlikely to fix a concurrency problem (if that's what it is), for which the simplest approach is to use Bluebird's Promise.map with the concurrency option.
The caller code can remain the same, just change getData(), as follows:
let getData = (urlList) => {
let concurrency = 10; // play with this value to find a reliable concurrency limit
return Promise.map(urlList, URL => axios.get(URL), {'concurrency': concurrency})
.catch(error => {
error.message = "There is a problem getting Axios array of promises " + error.message;
throw error;
});
};
// where `Promise` is Bluebird.
const axios = require('axios');
const axiosThrottle = require('axios-throttle');
//pass axios object and value of the delay between requests in ms
axiosThrottle.init(axios,200)
const options = {
method: 'GET',
};
const urlList = [
'https://jsonplaceholder.typicode.com/todos/1',
'https://jsonplaceholder.typicode.com/todos/2',
'https://jsonplaceholder.typicode.com/todos/3',
'https://jsonplaceholder.typicode.com/todos/4',
'https://jsonplaceholder.typicode.com/todos/5',
'https://jsonplaceholder.typicode.com/todos/6',
'https://jsonplaceholder.typicode.com/todos/7',
'https://jsonplaceholder.typicode.com/todos/8',
'https://jsonplaceholder.typicode.com/todos/9',
'https://jsonplaceholder.typicode.com/todos/10'
];
const promises = [];
const responseInterceptor = response => {
console.log(response.data);
return response;
};
//add interceptor to work with each response seperately when it is resolved
axios.interceptors.response.use(responseInterceptor, error => {
return Promise.reject(error);
});
for (let index = 0; index < urlList.length; index++) {
options.url = urlList[index];
promises.push(axiosThrottle.getRequestPromise(options, index));
}
//run when all promises are resolved
axios.all(promises).then(responses => {
console.log(responses.length);
});
https://github.com/arekgotfryd/axios-throttle

Wait for data from external API before making POST request

I'm using the IBM Watson Tone Analyser API with Express.js and React. I have this code which sends some test to the Watson API:
// tone-analyser.js
class ToneAnalysis {
constructor() {
const params = {
username: process.env.USERNAME,
password: process.env.PASSWORD,
version_date: '2018-01-31'
}
this.Analyzer = new ToneAnalyzerV3(params);
}
ToneAnalyser(input) {
let tones = this.Analyzer.tone(input, (err, tone) => {
if (err) console.log(err.message)
let voiceTone = tone.document_tone.tones[0].tone_id;
console.log(voiceTone) // Logs the right value on Node.js console
return voiceTone;
});
return tones;
}
}
module.exports = ToneAnalysis;
I then use this on my Express backend like so:
// server.js
const ToneAnalysis = require('./api/tone-analyser');
const app = express();
const input = {
tone_input: 'I am happy',
content_type: 'text/plain'
}
app.get('/api/tone', (req, res) => {
let tone = new ToneAnalysis().ToneAnalyser(input);
return res.send({
tone: tone
});
});
And I make an API call from React here:
// App.js
componentDidMount() {
this.callApi()
.then(res => {
console.log(res.tone); // Logs the wrong value on Chrome console
})
.catch(err => console.log(err));
}
callApi = async () => {
const response = await fetch('/api/tone');
const body = await response.json();
if (response.status !== 200) throw new Error(body.message);
console.log(body);
return body;
};
I expect the value of res.tone to be a string showing the tone gotten from the tone analysis function (new ToneAnalysis().ToneAnalyser(input);). Instead, I get
{
uri: {...},method: "POST", headers: {...}}
headers: {...},
uri: {...},
__proto__: Object
}
I think this happens because the res.send(...) runs before tone has a value from the API. My question is, how do I make res.send(...) run only after tone has a value?
I tried wrapping the callback function in this.Analyzer.tone(input, [callback]) in an async/await block, but that did not fix the issue. Any ideas on how to fix this will be highly appreciated. Thanks!
If the call to
let tone = new ToneAnalysis().ToneAnalyser(input);
returns a promise then you could do something like
tone.then(res.send.bind(res))
If the call to
let tone = new ToneAnalysis()`enter code here`.ToneAnalyser(input);
returns a promise then you could do something like
tone.then(res.send.bind(res))

How should I connect to a Redis instance from an AWS Lambda function?

I'm trying to build an API for a single-page web app using AWS Lambda and the Serverless Framework. I want to use Redis Cloud for storage, mostly for its combination of speed and data persistence. I may use more Redis Cloud features in the future, so I'd prefer to avoid using ElastiCache for this. My Redis Cloud instance is running in the same AWS region as my function.
I have a function called related that takes a hashtag from a GET request to an API endpoint, and checks to see if there's an entry for it in the database. If it's there, it should return the results immediately. If not, it should query RiteTag, write the results to Redis, and then return the results to the user.
I'm pretty new to this, so I'm probably doing something adorably naive. Here's the event handler:
'use strict'
const lib = require('../lib/related')
module.exports.handler = function (event, context) {
lib.respond(event, (err, res) => {
if (err) {
return context.fail(err)
} else {
return context.succeed(res)
}
})
}
Here's the ../lib/related.js file:
var redis = require('redis')
var jsonify = require('redis-jsonify')
var rt = require('./ritetag')
var redisOptions = {
host: process.env.REDIS_URL,
port: process.env.REDIS_PORT,
password: process.env.REDIS_PASS
}
var client = jsonify(redis.createClient(redisOptions))
module.exports.respond = function (event, callback) {
var tag = event.hashtag.replace(/^#/, '')
var key = 'related:' + tag
client.on('connect', () => {
console.log('Connected:', client.connected)
})
client.on('end', () => {
console.log('Connection closed.')
})
client.on('ready', function () {
client.get(key, (err, res) => {
if (err) {
client.quit()
callback(err)
} else {
if (res) {
// Tag is found in Redis, so send results directly.
client.quit()
callback(null, res)
} else {
// Tag is not yet in Redis, so query Ritetag.
rt.hashtagDirectory(tag, (err, res) => {
if (err) {
client.quit()
callback(err)
} else {
client.set(key, res, (err) => {
if (err) {
callback(err)
} else {
client.quit()
callback(null, res)
}
})
}
})
}
}
})
})
}
All of this works as expected, to a point. If I run the function locally (using sls function run related), I have no problems whatsoever—tags are read from and written to the Redis database as they should be. However, when I deploy it (using sls dash deploy), it works the first time it's run after deployment, and then stops working. All subsequent attempts to run it simply return null to the browser (or Postman, or curl, or the web app). This is true regardless of whether the tag I use for testing is already in the database or not. If I then re-deploy, making no changes to the function itself, it works again—once.
On my local machine, the function first logs Connected: true to the console, then the results of the query, then Connection closed. On AWS, it logs Connected: true, then the results of the query, and that's it. On the second run, it logs Connection closed. and nothing else. On the third and all subsequent runs, it logs nothing at all. Neither environment ever reports any errors.
It seems pretty clear that the problem is with the connection to Redis. If I don't close it in the callbacks, then subsequent attempts to call the function just time out. I've also tried using redis.unref instead of redis.quit, but that didn't seem to make any difference.
Any help would be greatly appreciated.
I've now solved my own problem, and I hope I can be of help to someone experiencing this problem in the future.
There are two major considerations when connecting to a database like I did in the code above from a Lambda function:
Once context.succeed(), context.fail(), or context.done() is called, AWS may freeze any processes that haven't finished yet. This is what was causing AWS to log Connection closed on the second call to my API endpoint—the process was frozen just before Redis finished closing, then thawed on the next call, at which point it continued right where it left off, reporting that the connection was closed. Takeaway: if you want to close your database connection, make sure it's fully closed before you call one of those methods. You can do this by putting a callback in an event handler that's triggered by a connection close (.on('end'), in my case).
If you split your code into separate files and require them at the top of each file, like I did, Amazon will cache as many of those modules as possible in memory. If that's causing problems, try moving the require() calls inside a function instead of at the top of the file, then exporting that function. Those modules will then be re-imported whenever the function is run.
Here's my updated code. Note that I've also put my Redis configuration into a separate file, so I can import it into other Lambda functions without duplicating code.
The Event Handler
'use strict'
const lib = require('../lib/related')
module.exports.handler = function (event, context) {
lib.respond(event, (err, res) => {
if (err) {
return context.fail(err)
} else {
return context.succeed(res)
}
})
}
Redis Configuration
module.exports = () => {
const redis = require('redis')
const jsonify = require('redis-jsonify')
const redisOptions = {
host: process.env.REDIS_URL,
port: process.env.REDIS_PORT,
password: process.env.REDIS_PASS
}
return jsonify(redis.createClient(redisOptions))
}
The Function
'use strict'
const rt = require('./ritetag')
module.exports.respond = function (event, callback) {
const redis = require('./redis')()
const tag = event.hashtag.replace(/^#/, '')
const key = 'related:' + tag
let error, response
redis.on('end', () => {
callback(error, response)
})
redis.on('ready', function () {
redis.get(key, (err, res) => {
if (err) {
redis.quit(() => {
error = err
})
} else {
if (res) {
// Tag is found in Redis, so send results directly.
redis.quit(() => {
response = res
})
} else {
// Tag is not yet in Redis, so query Ritetag.
rt.hashtagDirectory(tag, (err, res) => {
if (err) {
redis.quit(() => {
error = err
})
} else {
redis.set(key, res, (err) => {
if (err) {
redis.quit(() => {
error = err
})
} else {
redis.quit(() => {
response = res
})
}
})
}
})
}
}
})
})
}
This works exactly as it should—and it's blazing fast, too.

Resources