How to get a variable from another file after its defined - node.js

I'm trying to get the mongo client in another file. The problem is, when I try getting the mongoClient variable, it returns undefined.
How can I wait till a the mongoClient variable is declared before trying to get it?
File 1
let mongoClient;
module.exports = async function() {
const mongooseOptions = {...};
mongoClient = await mongoose.connect(dbUrl, mongooseOptions);
};
exports.getMongoClient = () => mongoClient;
File 2
const { getMongoClient } = require('../../startups/db');
console.log(getMongoClient); // Returns undefined

You should use this logic
File 1
const axios = require("axios");
async function getData() {
return await axios.get("https://jsonplaceholder.typicode.com/todos");
}
module.exports = { getData };
File 2
const { getData } = require("./file1");
getData().then(data => console.log(data));

You should use global variable for access in in any file like as bellow.
File 1
global.mongoClient;
module.exports = async function() {
const mongooseOptions = {...};
global.mongoClient = await mongoose.connect(dbUrl, mongooseOptions);
};
File 2
console.log(global.getMongoClient);

Related

NodeJS - Returning 'undefined'

I am learning NodeJS (generally I write in PHP). Please help me figure out the Promises. Now the 'getDataFromDir' function returns 'undefined'. I read the documentation, but apparently I don't fully understand something. Thanks in advance!
const host = 'localhost';
const port = 3000;
const __dirname = process.cwd();
async function getDataFromDir(fileName){
fsp
.readdir(path.join(fileName))
.then(async (indir) => {
const list = []
for (const item of indir) {
const src = await fsp.stat(path.join(fileName, item))
list.push(item)
}
return list;
})
}
const server = http.createServer(async (req, res) => {
const result = await getDataFromDir(__dirname);
result
.then((list) => {
console.log(list);
});
});
It seems like your return statement is returning only for the callback function under your .then statement. You should be able to use the await statement with your initial request and achieve a similar result.
async function getDataFromDir(fileName){
let indir = await fsp.readdir(path.join(fileName));
const list = [];
for (const item of indir) {
const src = await fsp.stat(path.join(fileName, item));
list.push(item);
}
return list;
}

Connecting to mongodb in nodejs

I am first to use MongoClient to connect mongodb in nodejs, in each js file I use it like following
'use strict'
//part 1
const { MongoClient } = require('mongodb');
const dbconfig = require('../config/index');
const Mongodb = {
client: new MongoClient(dbconfig.product.dbUrl, {
useNewUrlParser: true,
useUnifiedTopology: true,
}),
oper: null,
db: null,
dbName: '',
};
const dbConnect = async (dbName = dbconfig.product.dbName) => {
if (Mongodb.oper) {
if (dbName !== Mongodb.dbName) {
Mongodb.db = Mongodb.client.db(dbName);
Mongodb.dbName = dbName;
}
return Mongodb.db;
}
Mongodb.oper = await Mongodb.client.connect();
return await dbConnect(dbName);
};
//part 2
const db = await dbConnect();
let info = await db.collection.find({});
//more code
The situation is that there is a lot of duplicate code, such as part 1, and I want to put part 1 into a file and import it where needed. I have no idea how to do, give me some ideas please, thank you.
You only need to connect to db once. My advice would be - google and try mongoose. I find it easier. Google some examples and read the docs.
Create a JS module that exports the connect function using module.exports then require it where necessary, something like this:
// mongo.js
const { MongoClient } = require('mongodb');
const dbconfig = require('../config/index');
const client = new MongoClient(dbconfig.product.dbUrl, {
useNewUrlParser: true,
useUnifiedTopology: true
});
let databasePromise;
async function _connect() {
try {
await client.connect();
} catch (e) {
console.error(e);
await closeConnection();
throw e;
}
return client.db();
}
function connect() {
if (!databasePromise) {
databasePromise = _connect();
}
return databasePromise;
}
async function close() {
await client.close();
databasePromise = undefined;
}
isConnected() {
return client.isConnected();
}
module.exports = {
connect,
close,
isConnected,
}
// index.js
const { connect } = require('./mongo');
(async () => {
const db = await connect();
const results = await db.collection('collection1').find({});
console.log(results);
})();
// file2.js
const { connect } = require('./mongo');
(async () => {
const db = await connect();
const results = await db.collection('collection2').find({});
console.log(results);
})();
Note, this code is not tested so it might need adjustments.

nodeJS share variable between modules

i have a problem with my project, i need sharing the configurations parameters between modules, y have my entry point
app.js
const { requestConfiguracion } = require('./clases/servicios');
( async () => {
const dbConfig = await requestConfiguracion();
})();
servicios.js
const axios = require('axios').default;
const requestConfiguracion = async () => {
try {
const request = await axios.get('http://localhost/config/getConfig');
return request.data;
} catch (error) {
console.error(error)
}
}
module.exports = {
requestConfiguracion,
}
I need that the configuration in dbConfig is available for the other modules.
You can create a separated module to read and export that dbConfig.
// db-config.js
const { requestConfiguracion } = require('./clases/servicios');
let dbConfig = null;
module.exports.getDbConfig = async () => {
if (dbConfig) return dbConfig; // cache dbConfig for next time
dbConfig = await requestConfiguracion();
return dbConfig;
};
And in other modules:
// app.js
const { getDbConfig } = require('./db-config');
async function someFunction() {
const dbConfig = await getDbConfig();
// do something
}
Since your requestConfiguracion() function is an async function, you have to use await every time trying to get that config.

UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'map' of undefined

I'm here doing a scraper using node.js request and request-promise, cheerio.
My code:
const request = require("request");
const cheerio = require("cheerio");
const rp = require("request-promise");
const url = "https://singapore.craigslist.org/d/automotive-services/search/aos"
const scrapeResults = [];
async function scrapeJobHeader() {
try {
const htmResult = await rp.get(url);
const $ = await cheerio.load(htmResult);
$(".result-info").each((index, element) => {
const resultTitle = $(element).children(".result-title");
title = resultTitle.text();
link = resultTitle.attr("href");
const datePosted = $(element).children("time").attr("datetime");
const scrapResult = {title, link, datePosted};
scrapeResults.push(scrapResult);
return scrapeResults;
});
} catch (err) {
console.error(err);
}
}
async function scrapeDescription(jobWithHeaders) {
return await Promise.all(
jobWithHeaders.map(async job => {
const htmResult = await rp.get(job.url);
const $ = await cheerio.load(htmResult);
$(".print-qrcode-container").remove();
job.description = $("#postingbody").text();
})
);
}
async function scrapeCraigslist() {
const jobWithHeaders = await scrapeJobHeader();
const jobsFullData = await scrapeDescription();
console.log(jobFullData);
}
scrapeCraigslist();
When I run the code I get error like:
C:\Users\Ahmed-PC\craigslist>node index.js
(node:19808) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'map' of undefined
at scrapeDescription (C:\Users\Ahmed-PC\craigslist\index.js:42:24)
at scrapeCraigslist (C:\Users\Ahmed-PC\craigslist\index.js:62:32)
How I can fix this error and what wrong I'm doing here ?
You're doing this await scrapeDescription();, but you can't call that function without passing it an array.
When you do, then your argument jobWithheaders is undefined and you then try to do undefined.map() which gives you the error you see.
It looks like maybe you just need to change this:
async function scrapeCraigslist() {
const jobWithHeaders = await scrapeJobHeader();
const jobsFullData = await scrapeDescription();
console.log(jobFullData);
}
to this:
async function scrapeCraigslist() {
const jobWithHeaders = await scrapeJobHeader();
const jobsFullData = await scrapeDescription(jobWithHeaders); // <===
console.log(jobFullData);
}
Also, there's no reason to do:
return await Promise.all(...)
Change that to:
return Promise.all(...)
Either way, you're returning a promise that resolves to the same value. Basically, there's never any reason inside an async function to do return await somePromise. Just return the promise directly without the await. All the await does (if not optimized out by the interpreter) is wait for the promise to resolve, get value out of it, then take the promise that was already returned from the async function and make that value the resolved value of that promise. Which gives you the identical result as just returning the promise you already had without the await.
Change this:
const scrapeResults = [];
async function scrapeJobHeader() {
try {
const htmResult = await rp.get(url);
const $ = await cheerio.load(htmResult);
$(".result-info").each((index, element) => {
const resultTitle = $(element).children(".result-title");
title = resultTitle.text();
link = resultTitle.attr("href");
const datePosted = $(element).children("time").attr("datetime");
const scrapResult = {title, link, datePosted};
scrapeResults.push(scrapResult);
return scrapeResults;
});
} catch (err) {
console.error(err);
}
}
to this:
async function scrapeJobHeader() {
const scrapeResults = [];
const htmResult = await rp.get(url);
const $ = await cheerio.load(htmResult);
$(".result-info").each((index, element) => {
const resultTitle = $(element).children(".result-title");
const title = resultTitle.text();
const link = resultTitle.attr("href");
const datePosted = $(element).children("time").attr("datetime");
const scrapResult = {title, link, datePosted};
scrapeResults.push(scrapResult);
});
return scrapeResults;
}
And, then change this:
scrapeCraigslist();
to this:
scrapeCraigslist().then(results => {
// use the results in here only
console.log(results);
}).catch(err => {
console.log(err);
});
Then, change this:
async function scrapeDescription(jobWithHeaders) {
return await Promise.all(
jobWithHeaders.map(async job => {
const htmResult = await rp.get(job.url);
const $ = await cheerio.load(htmResult);
$(".print-qrcode-container").remove();
job.description = $("#postingbody").text();
})
);
}
to this:
function scrapeDescription(jobWithHeaders) {
return Promise.all(
jobWithHeaders.map(async job => {
const htmResult = await rp.get(job.url);
const $ = await cheerio.load(htmResult);
$(".print-qrcode-container").remove();
job.description = $("#postingbody").text();
return job;
});
);
}

Unit test with promise handling - node.js

i want to do a unit test with async function on the code. and here is my code on user.test.js
'use strict'
const UserDomain = require("../../../../../../bin/modules/users/repositories/commands/domain")
const UserHandler = require("../../../../../../bin/modules/users/repositories/commands/command_handler")
const expect = require('chai').expect;
const assert = require('chai').assert;
const sinon = require('sinon');
describe('User domain', () => {
describe('".login(data)"', () => {
let user;
beforeEach( () => {
user = {
clientId : "adithyavisnu",
clientSecret : "secretOfmine#19"
}
});
it("should return error when username/password is empty", (done)=> {
done();
// let
})
it("should return object", async () => {
const domainStub = sinon.stub(UserDomain.prototype, 'login');
const result = await UserHandler.login(user);
sinon.assert.calledOnce(domainStub);
domainStub.restore();
})
});
});
If the normal code (not the unit test code above) the const result = await UserHandler.login(user); will have an object response, but when i do in user.test.js it do not get the response. the result is undefined.
here are the user_handler code
'use strict';
const User = require('./domain');
const login = async (data) => {
const postData = async () => {
const user = new User();
const result = await user.login(data);
return result;
}
const response = await postData();
return response;
}
Is there something i did wrong on the code or some code is missing?
I am sorry if you do think there is unclear information
Thank you for the responses
In the normal flow, the UserHandler calls the Domain.login method and returns the result object. When you run the unit test you are stubbing the Domain.login method. so, it wont return the result as normal flow. You can either make the stub return some result object and test that or just spy the Domain.login instead of stubbing it , if you just want to just check that the Domain.login was called without altering its behavior. Read more on stubs/spies here if you would like - http://sinonjs.org/releases/v1.17.7/stubs/

Resources