Why is my code error not stopping the function in nodejs? - javascript

My self and another developer are working on an API using node.js and we are not advanced coders yet. We ran into problem. I will try and explain the problem here with sample references.
We have an API route that would be called on the frontend, please this is just a sample and not the real code. client said the codes should be private. We want to stop the function when an error is detected in another function we called. I am sure that we are not doing it rightly. Here is the API route for this post request and we called another function that we exported and imported here.
We simply want the httpCreateHarsh function to end if there is an error in the anotherFunction. With what we have, the error is seen in console.log when the user is not an admin for example but the httpCreateHarshfunction keeps running until it reaches the last line. Is this possible? Or is there another way we can structure the code to achieve this?
Shared sample of my code
const callAnotherFunction = require("../anotherfunction")
const httpCreateHarsh = async(req, res) => {
await callAnotherFunction(req, res);
return res.status(200).json('created')
}
//This is the second function we called:
const obj = {
status: 'success',
code: '244'
}
const anotherFunction = async(req, res) => {
if (req.body.user !== 'admin') {
return res.status(401).json('Unauthorized')
}
return obj
}
export default = anotherFunction
//The route here:
const express = require('express');
const router = express.Router();
const httpCreateHarsh = require('../httpCreateHarsh');
router.post("/harsh", httpCreateHarsh)

You couldn't return the res object in your second function.
To solve this problem you could throw exceptions and catch them in your handler function.
// request handler function
const httpCreateHarsh = async (req, res) => {
try {
await callAnotherFunction(req, res);
} catch (e) {
return res.status(401).json('Unauthorized')
}
return res.status(200).json('created')
}
const anotherFunction = asyn(req, res) => {
if (req.body.user !== 'admin') {
throw new Error('Unauthorized')
}
return obj
}

What you can do is you can wrap the code in httpCreateHarsh in an try...catch so whenever there is error inside it it will trigger the catch block and u exit the api.
const httpCreateHarsh = async(req, res)=>{
try{
await callAnotherFunction(req, res);
return res.status(200).json('created')
} catch(err){
return res.status(401).json('Unauthorized')
}
}
As an addition to this code you can return a promise from anotherFunction so that the catch block will be triggered once the promise is rejected.
For Exmaple:
const anotherFunction = async(req, res) => {
return new Promise(function(myResolve, myReject) {
if (req.body.user !== 'admin') {
myReject();
}
myResolve(obj);
});
}

If the code runs as you want it, it will generate the "cannot set headers after they are sent to the client" error, because you will be returning 2 responses
the first will be "unauthorized" by "anotherFunction" function and then the other response which is "created" of the current function "httpCreateHarsh".
what you should do instead is to call the "anotherFunction" as a middleware before moving to the "httpCreateHarsh" function.
it can be done this way:
// anotherfunction.js file containing the function you want to import
module.exports = {
async anotherFunction(req, res) {
if (req.body.user !== 'admin') {
return res.status(401).json('Unauthorized')
}
// this way, you can access this object from the "httpCreateHarsh" function by using req.body.obj
req.body.obj = {
status: 'success',
code: '244'
}
// this next indicates that there were no errors, and the next function will be called
next();
}
}
const httpCreateHarsh = async(req, res) => {
// do wathever you want here
return res.status(200).json('created')
}
//The route here:
const express = require('express');
const router = express.Router();
const httpCreateHarsh = require('../httpCreateHarsh');
const callAnotherFunction = require("../anotherfunction")
router.post("/harsh", (req, res, next) => callAnotherFunction(req, res, next), httpCreateHarsh)

Related

js imported function error "getUser not a function"

I have three files
BotJobActions.js
TestDate.js
CreateCron.js
The BotJobActions file creates a function called getUser that returns the user connected to a specific job, then exports the getUser along with a bunch of other functions.
const getUser = async (jobId) =>{
await mongoConnect(process.env.DB_PWORD)
try {
const user = await User.findOne({pendingJobs:jobId})
return user
} catch (err) {
console.log(err)
}
}
module.exports = { newJob, getUserJobs, getUser, updateUserJob, destroyUserPendingJob, destroyUserCompletedJob, activateJob, deactivateJob, endJob }
TestDate defines a function called runBot which runs a bot Job. In runBot it also calls the getUser function, so I can make changes to a specific user. Then exports the function because it will be used in other files.
const { getUser } = require("../bot/botJobActions");
const runBot = async (todayJobs) =>{
// await mongoConnect(process.env.DB_PWORD)
for(const job of todayJobs){
const clubPassword = decryptToken(job.clubPassword.token, job.clubPassword.iv)
const user = await getUser(job.id)
if(job.proxy){
const proxyConfig = await getProxyConfig(user)
if(proxyConfig.status === "no proxy") console.log("[-] Proxy Config Retrival Error/Running Without Proxy")
// await startBot(member=job.member?job.member : null, proxy=proxyConfig.status === 'success'?proxyConfig:null, job.clubUsername, clubPassword, job.startTime, job.endTime, job.courseList, job.id)
await console.log(member=job.member?job.member : null, proxy=proxyConfig.status === 'success'?proxyConfig:null, job.clubUsername, clubPassword, job.startTime, job.endTime, job.courseList, job.id)
}else{
// await startBot(member=job.member?job.member : null, proxy=null, job.clubUsername, clubPassword, job.startTime, job.endTime, job.courseList, job.id)
await console.log(member=job.member?job.member : null, proxy=null, job.clubUsername, clubPassword, job.startTime, job.endTime, job.courseList, job.id)
}
}
return
}
module.exports = { runBot, getJobs }
CreateCron is a function that runs whenever a job is created with a specific start time. This function will create a cron job for that specified time to run the bot.
const schedule = require('node-schedule');
const { runBot } = require('./testDate');
const createCron = (job) =>{
const startDate = new Date(job.botStartDate)
const startTime = new Date(`09/19/2000 ${job.botStartTime}`)
startDate.setHours(startTime.getHours())
startDate.setMinutes(startTime.getMinutes())
console.log(startDate.toUTCString())
schedule.scheduleJob(startDate, async function(){
console.log('run job')
await runBot([job])
})
}
My problem thought is that whenever I run the createCron function, I get an error saying that the getUser is not a function. Even thought it is.
Any help is appreciated!!
I was able to fix the problem. All I had to do was use the absolute path to the function instead of the relative path. Then the functions worked. Hope this can help somebody!

How do I properly route data through my Node API?

I have the following files:
My routes - where the orders_count route lives:
routes/index.js
const express = require('express');
const router = express.Router();
const transactionsController = require('../controllers/transactionsController');
const ordersController = require('../controllers/ordersController');
const ordersCountController = require('../controllers/ordersCountController');
router.get('/transactions', transactionsController);
router.get('/orders', ordersController);
router.get('/orders_count', ordersCountController);
module.exports = router;
I then have my orders count controller living in the controllers directory:
controllers/ordersCountController.js
const ordersCountService = require('../services/ordersCountService');
const ordersCountController = (req, res) => {
ordersCountService((error, data) => {
if (error) {
return res.send({ error });
}
res.send({ data })
});
};
module.exports = ordersCountController;
My controller then calls my order count service which fetches data from another API.
services/ordersService.js
const fetch = require('node-fetch');
// connect to api and make initial call
const ordersCountService = (req, res) => {
const url = ...;
const settings = { method: 'Get'};
fetch(url, settings)
.then(res => {
if (res.ok) {
res.json().then((data) => {
return data;
});
} else {
throw 'Unable to retrieve data';
}
}).catch(error => {
console.log(error);
});
}
module.exports = ordersCountService;
I'm trying to return the JSON response. I initially had it setup with requests but looking at the NPM site, it appears that it's depreciated so have been digging through how to use node-fetch.
I have tried both 'return data' and res.send({data}), but neither are solving the problem.
I am still new to this so I am likely missing something very obvious, but how come I am not sending the JSON back through so that it displays at the /api/orders_count endpoint?
I keep thinking I messed something up in my controller but have been looking at it for so long and can't seem to figure it out.
Any help would be greatly appreciated and if there is anything I can add for clarity, please don't hesitate to ask.
Best.
please learn promises and await syntax. life will be easier.
never throw a string. always prefer a real error object, like that : throw new Error('xxx'); that way you will always get a stack. its way easier to debug.
avoid the callback hell : http://callbackhell.com/
you need to decide if you want to catch the error in the controller or in the service. no need to do in both.
in the controller you call the service that way :
ordersCountService((error, data) => {
but you declare it like that :
const ordersCountService = (req, res) => {
which is not compatible. it should look like this if you work with callback style :
const ordersCountService = (callback) => {
...
if (error) return callback(error)
...
callback(null, gooddata);
here is an example to flatten your ordersCountService function to await syntax, which allows the "return data" you were trying to do :
const fetch = require('node-fetch');
// connect to api and make initial call
const ordersCountService = async (req, res) => {
const url = ...;
const settings = { method: 'Get'};
try {
const res = await fetch(url, settings);
if (!res.ok) throw new Error('Unable to retrieve data');
return await res.json();
} catch(error) {
console.log(error);
}
}
module.exports = ordersCountService;
in fact i would prefer to error handle in the controller. then this woud be sufficient as a service
const fetch = require('node-fetch');
// connect to api and make initial call
const ordersCountService = async () => {
const url = ...;
const settings = { method: 'Get'};
const res = await fetch(url, settings);
if (!res.ok) throw new Error('Unable to retrieve data');
return await res.json();
}
module.exports = ordersCountService;
then you can call this funtion like this :
try {
const data = await ordersCountService(req, res);
} catch(err) {
console.log(err);
}
//or
ordersCountService(req, res).then((data) => console.log(data)).catch((err) => console.error(err));

Calling multiple functions in single express route call?

I created a web scraping function with cheerio.js that I want to get called when the user opens the home page. I got it to work with one function, but I actually have 3 scraping functions that all do slightly different things. How do I add those functions in my route call to the home page along with the first function call? (basically i want all the data to be scraped upon the user opening the home page)
I'm also not very familiar with async/await or promises and got this code to work by following examples online, so if something is wrong there please let me know!
express route call that works (index.js):
const getSUPResults = require("./routes/SUPstores");
app.use(getSUPResults);
app.get("/", async function(req, res, next){
const result = await getSUPResults();
res.render("index", result);
});
scraper function ("./routes/SUPstores")
const express = require("express");
const cheerio = require("cheerio");
const { default: Axios } = require("axios");
const fetchData = async () => {
const result = await Axios.get("http://example.com");
return cheerio.load(result.data);
};
const getSUPResults = async () => {
const $ = await fetchData();
const SUPStoreInfo = [];
$('.row > tbody > tr:contains("SUP")').each(function(i, element){
const SUPResult = {
"environment" : $(this).children().text(),
"store" : $(this).children().next().text(),
"version" : $(this).children().next().next().text()
};
SUPStoreInfo.push(SUPResult);
});
return SUPStoreInfo;
}
module.exports = getSUPResults;
I tried to do something like this, but it only seemed to call the first function (getSUPResults), and ignored the rest. Is there something similar to this I could try?
const getSUPResults = require("./routes/SUPstores");
const getQAResults = require("./routes/QAstores");
const getDEVResults = require("./routesDEVstores");
app.use(getSUPResults);
app.use(getQAResults);
app.use(getDEVResults);
app.get("/", async function(req, res, next){
const SUP = await getSUPResults();
const QA = await getQAResults();
const DEV = await getDEVResults();
const result = [SUP, QA, DEV];
res.render("index", result);
});

MongoDB reusable custom javascript module

I would like to create a local Javascript module I can "require" in other files to handle all MongoDB CRUD operations.
I wrote something as:
-- dbConn.js file --
require('dotenv').config()
const MongoClient = require('mongodb').MongoClient
const ObjectID = require('mongodb').ObjectID
let _connection
const connectDB = async () => {
try {
const client = await MongoClient.connect(process.env.MONGO_DB_URI, {
useNewUrlParser: true,
useUnifiedTopology: true
})
console.log('Connected to MongoDB')
return client
} catch (err) {
console.log(error)
}
}
exports.findOne = async () => {
let client = await connectDB()
if (!client) {
return;
}
try {
const db = client.db("Test_DB");
const collection = db.collection('IoT_data_Coll');
const query = {}
let res = await collection.findOne(query);
return res;
} catch (err) {
console.log(err);
} finally {
client.close();
}
}
exports.findAll = async () => {
let client = await connectDB()
if (!client) {
return;
}
try {
const db = client.db("Test_DB");
const collection = db.collection('IoT_data_Coll');
const query = {}
let res = await collection.find(query).toArray();
return res;
} catch (err) {
console.log(err);
} finally {
client.close();
}
}
Then in another file (not necessary inside Express app), say
-- app.js ---
const findAll = require('./dbConn').findAll
const findOne = require('./dbConn').findOne
findAll().then(res => JSON.stringify(console.log(res)))
findOne().then(res => JSON.stringify(console.log(res)))
I wonder if it is correct?
I have to close the connection after each method/CRUD operation?
I was trying to use IIF instead of ".then", as:
(async () => {
console.log(await findOne())
})()
But I receive a weird error saying that findAll is not a function.
What's wrong with it?
Thanks.
It really depends on your use case which isn’t clear If you are using Express or just stand alone and how frequent are you planning to run app.js
Either way your code is expensive, each time you reference dbCon.js you are opening a new connection to the database.
So you can fix app.js by only requiring dbCon.js once and use it..
The best practice is to ofcourse use connection pooling https://www.compose.com/articles/connection-pooling-with-mongodb/

How to combine responses from two calls in one response?

I'm getting an empty response ({}), whereas my expected response is of format:
{
locationResponse: "location foo",
forecastResponse: "forecast bar"
}
In my index file I have:
const {getCity} = require('./routes/city');
const {getForecasts} = require('./routes/forecast');
app.get('/forecasts', function (req, res) {
var location = getCity(req, res);
var forecast = getForecasts(req, res);
//these are logged as undefined
console.log("Inside index.js");
console.log(location);
console.log(forecast);
res.send({locationResponse: location, forecastResponse: forecast});
});
Inside forecast file I have the following, and a similar one is in city file:
module.exports = {
getForecasts: (req, res) => {
var result = //mySQL DB calls and processing
console.log("Inside getForecasts");
console.log(result); //actual result printed
return "Forecast";
}
UPDATE: So I added some logs right before each call's return statements and figured out that the logs are printed in the following order, which means, it is not working as expected because I have not considered the fact that they are asynchronous calls.
Inside index.js
undefined
undefined
Inside getForecasts
{result}
The problem here is that in your ./routes/forecast/ getForecasts method, you're telling the response to send, with the data "Forecast". You should only ever use res.send once per request, as this will resolve the response and return to the client.
Instead, your getForecasts method should just return whatever data you need, and your index file should handle the response. If you need getForecasts to handle a response too, perhaps because you're sending requests directly to a forecasts endpoint that doesn't require location data, then you can refactor your code so that both index and forecasts make a call to get the data you need. For example:
/* index.js */
const {getCity} = require('./data/city');
const {getForecasts} = require('./data/forecast');
app.get('/forecasts', function (req, res) {
var location = getCity();
var forecast = getForecasts();
res.send({locationResponse: location, forecastResponse: forecast});
});
/* data/forecast.js */
module.exports = {
getForecasts: () => {
return "Forecast";
}
};
/* data/city.js */
module.exports = {
getCity: () => {
return "City";
}
};
Then you can also have:
/* routes/forecast.js */
const {getForecasts} = require('../data/forecast');
module.exports = {
getForecasts: (req, res) => {
res.send(getForecasts());
}
};
The above may be overcomplicating things, but I made the assumption that if you're using a routes directory, you probably want route handlers to be stored there. Hope this helps.
Seems both of your getCity() and getForecasts() functions are async. These asynchronous functions return a promise rather actual response.
So you can use simple asysn/await or Promise.all in JS to solve the issue.
Option 1: Use await for the promise to resolve before logging the message to the console:
app.get('/forecasts', async function (req, res) {
var location = await getCity(req, res);
var forecast = await getForecasts(req, res);
//these are logged as undefined
console.log("Inside index.js");
console.log(location);
console.log(forecast);
res.send({locationResponse: location, forecastResponse: forecast});
});
Option 2: Use Promise.all() to wait for all the promises to have fulfilled.
app.get('/forecasts', function (req, res) {
var list = await Promise.all([getCity(req, res), getForecasts(req, res)]);
//these are logged as undefined
console.log("Inside index.js");
console.log(list[0]);
console.log(list[1]);
res.send({locationResponse: list[0], forecastResponse: list[1]});
});
You can make use of async/await syntax.
app.get('/forecasts', async function (req, res) {
var location = await getCity(req, res);
var forecast = await getForecasts(req, res);
//these are logged as undefined
console.log("Inside index.js");
console.log(location);
console.log(forecast);
res.send({locationResponse: location, forecastResponse: forecast});
});

Categories

Resources