I’ve logger which I initiate using a constractor in the index.js file. Now I need
To pass the logger instance to other files, and I do it like this
index.js
const books = require(“./books”);
books(app, logger);
logger = initLogger({
level: levels.error,
label: “app”,
version: "0.0.1",
});
app.listen(port, () => logger.info(`listening on port ${port}`));
And inside the books.js file I use it like following, get the logger from the index.js file and use it
inside the books.js file, also pass it to another file with the function isbn.get(books, logger);,
Is it recommended to do it like this? Is there a cleaner way in nodes ?
books.js
const isbn = require(“./isbn”);
module.exports = async function (app, logger) {
…
try {
Let books = await getBooks();
logger.info(“get “books process has started”);
} catch (err) {
logger.error("Failed to fetch books", err);
return;
}
…
// this function is from the file “isbn” and I should pass the logger to it also
try {
let url = await isbn.get(books, logger);
} catch (e) {
res.send(e.message);
}
}
Try creating a module specifically for your logger configuration, then you can import that into your modules instead of using a side-effect of your business module to create a logger.
This will help if you ever need/want to change your logger configuration - instead of following a chain of business methods, you can just update the log configuration.
Example
logger.js
'use strict';
// Any setup you need can be done here.
// e.g. load log libraries, templates etc.
const log = function(level, message) {
return console.log(level + ": " + message);
};
module.exports = log;
business-logic.js
'use strict';
var log = require('./logger');
var stuff = require('./stuff');
const do_stuff = function (thing) {
// do stuff here
log("INFO", "Did stuff");
}
This is a pretty clean way of doing it, however it could be awkward when trying to share more variables or adding more requires. So, you could put all the variables in an object and destructure only the variables you need in books.js:
index.js:
const state = {app, logger, some, other, variables};
require("./books")(state);
require("./another_file")(state);
books.js:
module.exports = async function ({app, logger}) {
};
Related
I create for my project a log module and i am actually creating a new instance in all module to allow them to log in cli with the right syntax,color conf etc.
For example (a simplified example)
// index.js
const {Log,Ansi} = require("./class/log.js");
const Tool = require("./class/tool.js");
const argv = require("yargs").argv;
let log = new Log({
levelIcon:true,
powerlineRoot:{
name:"root",
backgroundColor:Ansi.BLACK_BRIGHT,
text: "myappName"
}
});
let tool = new Tool(argv.toolName,argv.envName)
tool.run().then(() => {
log.print("Tool is running","info");
}).catch((err) => {
log.print(err,"critical");
});
// tool.js
const {Log,Ansi} = require("./log.js");
class Tool {
let log = new Log({
levelIcon:true,
powerlineRoot:{
name:"root",
backgroundColor:Ansi.BLACK_BRIGHT,
text: "myappName"
}
});
run(){
return new Promise((resolve, reject) => {
resolve()
}
}
}
module.exports = Tool
I am wondering if there is a way to create only one instance in my index.js and share it with the instance of modules like Tools. I don't know if it's possible but i think that it will be less memory consumption to share one instance of Log than creating multiple one
I hope that my question is enough clear. Feel free to ask me more information if needed
Yes you can do that absolutely. Since the entry is index.js you can consider it will finally run as if it was a single file, in a single thread. You can create one more module logger.js like:
const {Log,Ansi} = require("./class/log.js");
const logger = new Log({
levelIcon:true,
powerlineRoot:{
name:"root",
backgroundColor:Ansi.BLACK_BRIGHT,
text: "myappName"
}
});
module.exports = logger;
Now you can just import logger and use it like:
const logger = require("./logger")
logger.print("hello world!");
Like #jjsingh says i use global variable to store the object.
Not sure it's the better way but for the moment it resolve my issue.
global.log = new Log({
levelIcon:true,
powerlineRoot:{
name:"root",
color:{
background:Ansi.BLUE_SEA,
foreground:Ansi.RED,
},
text:global.conf.get("infos").name
}
});
I am trying to export database properties stored in properties file from Javascript module. By the time I read database properties file, Javascript file is already exported and data properties appear undefined wherever I use in other modules.
const Pool = require('pg').Pool;
const fs = require('fs')
const path = require('path');
class DbConfig {
constructor(dbData) {
this.pool = new Pool({
user: dbData['user'],
host: dbData['host'],
database: dbData['database'],
password: dbData['password'],
max: 20,
port: 5432
});
}
}
function getdbconf() {
const dbData = {};
fs.readFile("../../db_properties.txt"), 'utf8', (err, data) => {
if (err) {
console.error(err)
return
}
// dbData = {"user":"postgres", "password": "1234"...};
return dbData;
});
}
let db = new DbConfig(getdbconf());
let dbPool = db.pool;
console.log("dbpool : -> : ",dbPool); // username and password appear undefined
module.exports = { dbPool };
Is there a way to read data before exporting data from Javascript module?
Usually database config or any other sensitive info is read from a .env file using dotenv .
Or
you could also provide env from command line itself like
DB_HOST=127.0.0.1 node index.js
inside your index.js
console.log(process.env.DB_HOST)
Please create a new file (connection-pool.js) and paste this code:
const { Pool } = require('pg');
const poolConnection = new Pool({
user: 'postgresUserName',
host: 'yourHost',
database: 'someNameDataBase',
password: 'postgresUserPassword',
port: 5432,
});
console.log('connectionOptions', poolConnection.options);
module.exports = poolConnection;
For use it, create a new file (demo-connection.js) and paste this code:
const pool = require('./connection-pool');
pool.query('SELECT NOW();', (err, res) => {
if (err) {
// throw err;
console.log('connection error');
return;
}
if (res) {
console.log(res.rows);
pool.end();
}
});
This is an alternative option 🙂
Exporting the result of async calls
To export values which have been obtained asynchronously, export a Promise.
const fs = require('fs/promises'); // `/promise` means no callbacks, Promise returned
const dbDataPromise = fs.readFile('fileToRead')); //`readFile` returns Promise now
module.exports = dbDataPromise;
Importing
When you need to use the value,
const dbDataPromise = require('./dbdata');
async init() {
const dbData = await dbDataPromise;
}
//or without async, using Promise callbacks
init() {
dbDataPromise
.then(dbData => the rest of your code that depends on dbData here);
}
Current code broken
Please note that your current code, as pasted above, is broken:
function getdbconf() {
const dbData = {};
fs.readFile("../../db_properties.txt"), 'utf8', (err, data) => {
//[...] snipped for brevity
return dbData;
});
}
fs.readFile "returns" dbData, but there is nothing to return to, since you are in a callback which you did not call yourself. Function getdbconf returns nothing.
The line that says let db = new DbConfig(getdbconf()); will NOT work. It needs to be inside the callback.
The only way to avoid putting all of your code inside the callback (and "flatten" it) is to use await, or to use readFileSync
Avoiding the issue
Using environment variables
Suhas Nama's suggestion is a good one, and is common practice. Try putting the values you need in environment variables.
Using synchronous readFile
While using synchronous calls does block the event loop, it's ok to do during initialization, before your app is up and running.
This avoids the problem of having everything in a callback or having to export Promises, and is often the best solution.
I have a function that looks like this
let fileCache = {};
async function generateFromFile(fullPath) {
if (!fileCache[fullPath]) {
let jsonResource = await fs.readFile(fullPath);
fileCache[fullPath] = JSON.parse(jsonResource);
}
return fileCache[fullPath];
}
When I use it in the same module like this
await generateFromFile('myfile.json');
it works fine.
I created a separate file, put the function there and exported it like this
let fileCache = {};
async function generateFromFile(fullPath) {
if (!fileCache[fullPath]) {
let jsonResource = await fs.readFile(fullPath);
fileCache[fullPath] = JSON.parse(jsonResource);
}
return fileCache[fullPath];
}
module.exports = {
generateFromFile: generateFromFile
}
I imported it like this
const utils = require('../util/utils.js');
and wanted to use like await utils.generateFromFile('myfile.json'); but it fails with the error
TypeError [ERR_INVALID_CALLBACK]: Callback must be a function. Received undefined
What is the problem?
How did you import your fs module. To use readFile with await your should
either write fs.promise.readFile
or import fs as
import { promises as fs } from 'fs'
Are you sure that your newly created file is in the relative directory ../util/utils.js? Your export looks fine. Maybe try to print out the imported utils object to see what you actually imported.
EDIT: You are also passing relative filenames to your function which is now in a different diretory than before. When accessing fs.readFile(fullPath) it tries to read the file inside the directory "util/". You should call your function with a full filepath like util.generateFromFile(path.join(__dirname, "myfile.json")) using the path node package.
EDIT 2: Like Raeesaa state you are using fs.readFile wrong. It does not return a Promise but expects a callback. To is it with await you have to promisify it like so:
const fs = require("fs");
const { promisify } = require("util");
const readFileAsync = promisify(fs.readFile);
let fileCache = {};
async function generateFromFile(fullPath) {
if (!fileCache[fullPath]) {
let jsonResource = await readFileAsync(fullPath);
fileCache[fullPath] = JSON.parse(jsonResource);
}
return fileCache[fullPath];
}
module.exports = {
generateFromFile: generateFromFile
}
EDIT 3: If you only import JSON files you can do it by using require. Internally, require has also a cache so imports are only executed once. You can import JSON files as objects like so: const obj = require("myfile.json");
I have a lot of function in my index.js for my webhook, and i wish split functions to different js files to my code is more clean.
I have not problem to use agent, but I don't know how I can have an access of variable "request" (to have the parameters receipt) in another file.
I tried this :
Index.js
// See https://github.com/dialogflow/dialogflow-fulfillment-nodejs
// for Dialogflow fulfillment library docs, samples, and to report issues
'use strict';
const functions = require('firebase-functions');
const { WebhookClient } = require('dialogflow-fulfillment');
const { Payload } = require('dialogflow-fulfillment');
const Test = require("./Test"); //File with an function
process.env.DEBUG = 'dialogflow:debug'; // enables lib debugging statements
exports.dialogflowFirebaseFulfillment = functions.https.onRequest((request, response) => {
const agent = new WebhookClient({ request, response });
function fallback(agent) {
agent.add(`I didn't understand`);
agent.add(`I'm sorry, can you try again?`);
}
}
let intentMap = new Map();
intentMap.set("intent-test", Test.welcome);
intentMap.set("intent-test", fallback);
agent.handleRequest(intentMap);
});
Test.js
exports.welcome = (agent) => {
agent.add("Hello World !");
console.log(request); // How can I have a access to the variable "request" / "response
};
Do you have a solution please ?
While I understand that you are not having any problems with your agent, there are some limitations when using the DialogFlow Inline editor. As you can see below, one of the limitations is that you can only work with two files: index.js and package.json. That means that all your Fulfillments should be inside your index.js file and you can't split it in more files.
Thank you in advance for your help.
I am unsure how to do the following. I have a module to send emails, and the Config is injected into the module using summon.js dependency injection, but I need to use the sendMail method and pass it the parameter mailOptions. Here is the code example:
'use strict';
const nodemailer = require('nodemailer');
const ejs = require('ejs');
const fs = require('fs');
module.exports = function(Configs) {
// create reusable transporter object using the default SMTP transport
let transporter = nodemailer.createTransport({
host: Configs.email.host,
port: Configs.email.port,
auth: {
user: Configs.email.user,
pass: Configs.email.pass
}
});
this.sendMail = function(mailOptions) {
mailOptions.to = Configs.mockEmail || mailOptions.to
mailOptions.from = Configs.email.user
return new Promise((resolve, reject) => {
if (mailOptions.template) {
ejs.renderFile('/../templates/' + mailOptions.template +
'.ejs', mailOptions.data, null, (err, html) => {
if (err) {
return reject(err)
}
resolve(html)
})
return
}
resolve()
}).then(html => {
mailOptions.html = html || mailOptions.html
return new Promise((resolve, reject) => {
// send mail with defined transport object
transporter.sendMail(mailOptions, (error, info) => {
if (error) {
return reject(error)
}
resolve(info)
})
})
})
}
return this
}
Then, I want to make use of this module:
const EmailUtil = require('email')
async function foo() {
// Do something async with await.
const mailOptions = {...}
EmailUtils.sendMail(mailOption);
}
However, it gives me the error:
TypeError: EmailUtils.sendMail is not a function
Note: I can remove the module.export = function(Configs) but them that will not be good since i would need to hard code the path of my config file and I have multiple configuration files for each environment. Then, I want to be able to keep Summon.js dependency injection while calling sendMail from another module. Thanks
Any ideas??
Thank you!
Since you're exporting a function you need to actually call it after requiring the module:
const EmailUtil = require('email')
async function foo() {
// Do something async with await.
const mailOptions = {...}
EmailUtil().sendMail(mailOption);
}
The use of this suggests that a function is supposed to be used as a constructor. There will be desired this object only if a function is called with new or bound to some context.
There is a convention in JavaScript to use pascal-cased names for constructors, so they could be identified unambiguously in the code.
For given EmailUtil, it should be:
const EmailUtil = require('email');
const emailUtil = new EmailUtil(config);
...
emailUtil.sendMail(mailOption);
I answer this by including EmailUtils in the depend.json file which takes care of defining the dependencies for summonjs. In this way, I was able to pass the Configs to EmailUtils and call sendMail in such way.
EmailUtils.sendMail(mailOptions);
There was no need to use the keyword new, which is a great answer. I was not aware a module could be instantiated in this way.