NodeJS: Controlling the order of loading modules - javascript

Here is the scenario:
I have 3 files (modules):
app.js
(async () => {
await connectoDB();
let newRec = new userModel({
...someprops
});
await newRec.save();
})();
The app.ts is the entry point of the project.
database.ts
interface ConnectionInterface {
[name: string]: mongoose.Connection;
}
export class Connection {
public static connections: ConnectionInterface;
public static async setConnection(name: string, connection: mongoose.Connection) {
Connection.connections = {
...Connection.connections,
[name]: connection,
};
}
}
export async function connectToDB() {
const conn = await mongoose.createConnection('somePath');
await Connection.setConnection('report', conn);
}
model.ts
const userSchema = new mongoose.Schema(
{
..someprops
},
);
const userModel = Connection.connections.report.model('User', userSchema);
export default userModel;
What I am trying to do: I need to have multiple mongoose connections, so I use an static prop called connections in Connection class (in database.ts); every time that I connect to a database I use setConnection to store the connection in mentioned static prop, so I can access it from every module in my project by its name which is report in this case.
Later, In model.ts I use Connection.connections.report to access the connection report to load my model!
Then, When I run app.ts I get the following error which is logical:
const aggregationModel = Connection.connections.report.model('User', userSchema)
^
TypeError: Cannot read property 'report' of undefined
The reason that causes this (I think) is, while loading imported modules in app.ts, .report is not declared because the app.ts isn't run completely (connectoDB() defines the .report key).
The codes that I have mentioned have been simplified for preventing complexity. The original app is an express app!
Now, How should I solve this error?
Thanks in advance.

You can wait for the connection to finish before using it if you change up your class slightly.
const connection = await Connection.getConnection()
const model = connection.example
...
class Connection {
...
public static async getConnection() => {
if (!Connection.connection) {
await Connection.setConnection()
}
return Connection.connection
}
}

Related

What design pattern to use when object create multiple time and share to different file?

enter code hereI have case i needs 2 connector MongoClient. first client to connect to local mongodb server. 2nd client to connect to remote mongodb. 2 client inside 1 file database-connector.js. is it right or wrong in this ways? what the correct ways in this problem?
class CatchError {
static error: string | unknown;
static save(error: string | unknown) {
this.error = error;
const re = new RegExp('ECONNREFUSED', 'i')
if (re.test(error.message)) {
// fs.write('system.error.log')
}
}
}
class DBClient {
client: MongoClient;
constructor(url: string, config: Partial<MongoClientOptions>) {
this.client = new MongoClient(url, config)
}
async connect() {
try {
this.client = await this.client.connect();
return this.client;
} catch(error) {
CatchError.parse(error)
}
}
}
// for entire data application
export const localDBClient = initializeDBConnection("mongodb://localhost");
// only for authentication to access application
export const remoteDBClient = initializeDBConnection("mongodb+srv://se132.aws-slcuster.com");
gui.js
import { remoteDBClient } from "database-connector.js";
// use client //
model.js
import { localDBClient } from "database-connector.js";
// use client //

How to get the current module's file path in a node module?

I want to print the correct filepath even if the function is imported in some other module inorder to handle the errors correctly. How can I do that? I am using serverless stack.
Please refer the following code,
class Logger {
filePath: string;
constructor(fp: string) {
filePath = fp;
}
printLog(info) {
const { timestamp, message } = info;
return `${timestamp} ${filePath}: ${message}`;
}
}
This is used in dbConnection.ts as,
const logger = new Logger(__filename);
export const connectToDB = () => {
try {
//DB connection code.
} catch(error) {
logger.print({ timestamp: new Date().toISOString(), message: error.message });
}
};
Now, I want to connect to db from some other module lets say, test.ts then I will use it as follows,
export const test = () => {
//some code here...
connectToDB();
}
When there occurs an error while connecting to DB, then It prints something like this,
2022-05-27T05:24:47.548Z src/test.ts: Error in connecting DB url is unreachable please check your internet connection.
In order to have proper debuggability, I want to print the filename from where the exception is actually thrown. That is src/dbConnection.ts and not src/test.ts.
Try using
__filename
__filename: This will return the path of the file executing
__dirname: This will return the path of the directory in which the file executing is located.
Check if it does what you need like
console.log(__filename);
Try to change filePath to this.filePath in your Logger Class

Hooks.js running the db connection and results twice in sveltekit

I'm using sveltekit and trying to understand all the new features added after retiring Sapper. One of those new features is hooks.js which runs on the server and not accessible to the frontend. It makes dealing with db safe. So I created a connection to my mongodb to retrieve user's data before I use the db results in my getSession function. It works but I noticed that it access my database TWICE. Here is my hooks.js code:
import * as cookie from 'cookie';
import { connectToDatabase } from '$lib/mongodb.js';
export const handle = async ({event, resolve})=>{
const dbConnection = await connectToDatabase();
const db = dbConnection.db;
const userinfo = await db.collection('users').findOne({ username: "a" });
console.log("db user is :" , userinfo) //username : John
const response = await resolve(event)
response.headers.set(
'set-cookie', cookie.serialize("cookiewithjwt", "sticksafterrefresh")
)
return response
}
export const getSession = (event)=>{
return {
user : {
name : "whatever"
}
}
}
The console.log you see here returns the user data twice. One as soon as I fire up my app at localhost:3000 with npm run dev and then less than a second, it prints another console log with the same information
db user is : John
a second later without clicking on anything a second console.log prints
db user is : John
So my understanding from the sveltekit doc is that hooks.js runs every time SvelteKit receives a request. I removed all prerender and prefetch from my code. I made sure I only have the index.svelte in my app but still it prints twice. My connection code I copied from an online post has the following:
/**
* Global is used here to maintain a cached connection across hot reloads
* in development. This prevents connections growing exponentially
* during API Route usage.
*/
Here is my connection code:
import { MongoClient } from 'mongodb';
const mongoURI ="mongodb+srv://xxx:xxx#cluster0.qjeag.mongodb.net/xxxxdb?retryWrites=true&w=majority";
const mongoDB = "xxxxdb"
export const MONGODB_URI = mongoURI;
export const MONGODB_DB = mongoDB;
if (!MONGODB_URI) {
throw new Error('Please define the mongoURI property inside config/default.json');
}
if (!MONGODB_DB) {
throw new Error('Please define the mongoDB property inside config/default.json');
}
/**
* Global is used here to maintain a cached connection across hot reloads
* in development. This prevents connections growing exponentially
* during API Route usage.
*/
let cached = global.mongo;
if (!cached) {
cached = global.mongo = { conn: null, promise: null };
}
export const connectToDatabase = async() => {
if (cached.conn) {
return cached.conn;
}
if (!cached.promise) {
const opts = {
useNewUrlParser: true,
useUnifiedTopology: true
};
cached.promise = MongoClient.connect(MONGODB_URI).then((client) => {
return {
client,
db: client.db(MONGODB_DB)
};
});
}
cached.conn = await cached.promise;
return cached.conn;
So my question is : is hooks.js runs twice all the time, one time on the server and one time on the front? If not, then why the hooks.js running/printing twice the db results in my case?
Anyone?

Collection object in Discord.js emptying after being successfully set

So I am running into an issue with my collections object in the Discord.js library. I am working on a command and event handler to populate a collection of commands and events based on the files written in each directory. Each collection appears to populate properly as long as I check it within the map function. Immediately after the second map is complete, whichever collection I populate second becomes empty, yet the first collection remains set.
If I reverse the order they are set, the problem changes to whichever collection is set second. And because they both set fine when debugged within the map or if they are set first, I am confident it is not related to anything directory wise or with the files they are importing. I suspect it is somehow related to how collections work on an object that I am unaware of.
Any insight to this would be great!
import { Command, Event, Config } from "../Interfaces/index"
import { Client, Collection, Intents } from "discord.js"
import glob from "glob";
import { promisify } from "util";
const globPromise = promisify(glob)
class Bot extends Client {
public events: Collection<string, Event> = new Collection()
public commands: Collection<string, Command> = new Collection()
public aliases: Collection<string, Command> = new Collection()
public config: Config
public constructor() {
super({ ws: { intents: Intents.ALL } })
}
public async init(config: Config): Promise<void> {
this.config = config
this.login(this.config.token)
const commandFiles: string[] = await globPromise(`${__dirname}/../Commands/**/*.ts`)
commandFiles.map(async (filePath: string) => {
const { command }: { command: Command } = await import(filePath)
this.commands.set(command.name, command)
if (command.aliases?.length !== 0) {
command.aliases?.forEach((alias) => {
this.aliases.set(alias, command)
})
}
})
const eventfiles: string[] = await globPromise(`${__dirname}/../Events/**/*.ts`)
eventfiles.map(async (filePath: string) => {
const { event }: { event: Event } = await import(filePath)
this.events.set(event.name, event)
console.log(this) // Events and commands collection are populated
})
console.log(this) // Events collection is empty and commands collection is populated
}
}
You may not know, but you mapped each of the commandFiles and eventFiles items to a Promise. To make sure that the async function was actually completed before you called console.log(this) you need to await the Promises returned by the map function.
To await every item that is returned from the map, wrap the call in Promise.all:
const commandFiles: string[] = await globPromise(`${__dirname}/../Commands/**/*.ts`)
await Promise.all(
commandFiles.map(async (filePath: string) => {
...
})
);
const eventfiles: string[] = await globPromise(`${__dirname}/../Events/**/*.ts`)
await Promise.all(
eventfiles.map(async (filePath: string) => {
...
})
);
console.log(this) // collections should be populated at this point
// because you awaited the `map` results
Leaving 'dangling' Promises can very often lead to some unexpected errors.

How to properly reuse MongoDriver connection across NodeJS modules with ES6

First of all it is a similar question like How to properly reuse connection to Mongodb across NodeJs application and modules, but I guess due to the ES6 syntax it's still different.
So I decided to use MongoDriver and created a class for this like in SO answer: dbconnections.js
import { default as mongodb } from 'mongodb';
const MongoClient = mongodb.MongoClient;
const url = "myurl"
let _db;
export const connectToServer = async (callback) => {
try {
MongoClient.connect( url, { useNewUrlParser: true, useUnifiedTopology:true }, ( err, db ) => {
_db = db
return callback( err )
})
} catch (e) {
throw e
}
}
export const getDB = () => _db
export const disconnectDB = () => _db.close()
The problem with this module is, that due to the ES6 syntax is that I can't make like something this
import {getDB} from '../dbconnections.js'
const driverDB=getDB()
export const someFunction= async (req,res) => {
console.log(driverDB)
because I always get undefiened so I have to call my getter in every function.
So my question: how to create properly a structure for a MongoDriver handler and pass this to different modules with ES6?
Pass the client object into objects and functions that need it.
https://en.wikipedia.org/wiki/Dependency_injection

Categories