MongoDB connection is not connecting to client in localhost - mongodb

Setting up a MEAN stack project I'm trying to connect mongodb connection but it is failing. Below is my code. Let me know if any changes required here. Thank you
In my connect.ts file
import { MongoClient } from 'mongodb';
export class MongoUtility {
static readonly collectionNames = {
stockList: 'StockList'
};
private _mongoClient: MongoClient;
private _tradeTrackDb;
private _connectionStatus: ConnectionStatus;
constructor() {
this._mongoClient = new MongoClient('mongodb://localhost:27017');
this._init();
}
private _init() {
this._connectDb();
}
private _connectDb() {
this._connectionStatus = ConnectionStatus.Connecting;
this._mongoClient.connect()
.then(client => {
const db = client.db().collection('TradeTrack');
this._tradeTrackDb = db;
this._tradeTrackDb.on('close', () => {
this._connectionStatus = ConnectionStatus.Reconnecting;
console.log('Lost connection to TradeTrackDB. Attempting to reconnect...');
});
this._tradeTrackDb.on('reconnect', () => {
this._connectionStatus = ConnectionStatus.Connected;
console.log('Recovered connection to TradeTrackDB.');
})
}).catch((error) => {
console.warn('Initial connection attempt to TradeTrackDB failed. Attempting reconnect in 5 seconds.', JSON.stringify(error));
setTimeout(this._connectDb.bind(this), 5000);
})
}
}
enum ConnectionStatus {
Connecting,
Connected,
Reconnecting
}
Its coming to catch of connectDb method everytime.
Error
Initial connection attempt to TradeTrackDB failed. Attempting reconnect in 5 seconds. {"reason":{"type":"Unknown","servers":{},"stale":false,"compatible":true,"heartbeatFrequencyMS":10000,"localThresholdMS":15,"setName":null,"maxElectionId":null,"maxSetVersion":null,"commonWireVersion":0,"logicalSessionTimeoutMinutes":null}}

Related

How to set run time variable to postgresql in typeorm and nest js

Iam using the row level security in supabase with nest.js, So how can I set runtime variables safely to the DB so that I can be sure that the variables sync with each app user (due to the http request triggered the execution)?
I saw that it is possible to set local variables in a transaction but I wouldn't like to wrap all the queries with transactions.
Thanks & Regards
I tried to execute this with subscribers in nestjs it working fine . but it wont have a function like beforeSelect or beforeLoad , so i drop it
import { Inject, Injectable, Scope } from '#nestjs/common';
import { InjectDataSource } from '#nestjs/typeorm';
import { ContextService } from 'src/context/context.service';
import { DataSource, EntityManager, LoadEvent, RecoverEvent, TransactionRollbackEvent, TransactionStartEvent } from 'typeorm';
import {
EventSubscriber,
EntitySubscriberInterface,
InsertEvent,
UpdateEvent,
RemoveEvent,
} from 'typeorm';
#Injectable()
#EventSubscriber()
export class CurrentUserSubscriber implements EntitySubscriberInterface {
constructor(
#InjectDataSource() dataSource: DataSource,
private context: ContextService,
) {
dataSource.subscribers.push(this);
}
async setUserId(mng: EntityManager, userId: string) {
await mng.query(
`SELECT set_config('request.jwt.claim.sub', '${userId}', true);`,
);
}
async beforeInsert(event: InsertEvent<any>) {
try {
const userId = this.context.getRequest();
await this.setUserId(event.manager, userId);
} catch (err) {
console.log(err);
}
}
async beforeTransactionRollback(event: TransactionRollbackEvent) {
console.log('hello')
try {
const userId = this.context.getRequest();
await this.setUserId(event.manager, userId);
} catch (err) {
console.log(err);
}
}
async beforeUpdate(event: UpdateEvent<any>) {
try {
const userId = this.context.getRequest();
await this.setUserId(event.manager, userId);
} catch (err) {
console.log(err);
}
}
async beforeRemove(event: RemoveEvent<any>) {
try {
const userId = this.context.getRequest();
await this.setUserId(event.manager, userId);
} catch (err) {
console.log(err);
}
}
}
After i get to know that we can use query runner instead of subscriber . but its not working ,
also i need a common method to use all the queries
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { Users } from 'src/common/entities';
import { DataSource, EntityManager, Repository } from 'typeorm';
#Injectable()
export class UsersService {
constructor(
#InjectRepository(Users) private userRepository: Repository<Users>,
private dataSource: DataSource,
private em: EntityManager,
) {}
getAllUsers(userId: string) {
const queryRunner = this.dataSource.createQueryRunner();
return new Promise(async (resolve, reject) => {
let res: any;
try {
await queryRunner.connect();
await queryRunner.manager.query(
// like this we can set the variable
`SELECT set_config('request.jwt.claim.sub', '${userId}', true);`,
);
// after setting config variable the query should return only one user by userId
res = await queryRunner.query('SELECT * FROM users');
// but it reurns every user
} catch (err) {
reject(err);
} finally {
await queryRunner.manager.query(`RESET request.jwt.claim.sub`);
await queryRunner.release();
resolve(res);
}
});
}
}
Thanks in advance....
Sorry to say, bro. But in currently state of development TypeORM does not have a feature that let us set conection variables. The roundabout for your problem is to do something like this.
/**
* Note: Set current_tenant session var and executes a query on repository.
* Usage:
* const itens = = await tenantTransactionWrapper( manager => {
* return manager.getRepository(Entity).find();
* });
*
* #param {function} callback - a function thar receives an Entity Manager and returns a method to be executed by tenantTransactionWrapper
* #param {string} providedTenantId - optional tenantId, otherwise tenant will be taken from localStorage
*/
async function tenantWrapper<R>(
callback: (manager: EntityManager) => Promise<R>,
providedTenantId?: string,
) {
const tenantId = providedTenantId || tenantStorage.get();
let response: R;
await AppDataSource.transaction(async manager => {
await manager.query(`SET LOCAL smsystem.current_tenant='${tenantId}';`);
response = await callback(manager);
});
return response;
}
Then create a custom repository to make use of the wraper a little bit simple.
const customRepository = <T>(entity: EntityTarget<T>) => ({
find: (options?: FindManyOptions<T>) =>
tenantTransactionWrapper(mng => mng.getRepository(entity).find(options))(),
findAndCount: (options?: FindManyOptions<T>) =>
tenantTransactionWrapper(mng =>
mng.getRepository(entity).findAndCount(options),
)(),
save: (entities: DeepPartial<T>[], options?: SaveOptions) =>
tenantTransactionWrapper(mng =>
mng.getRepository(entity).save(entities, options),
)(),
findOne: (options: FindOneOptions<T>) =>
tenantTransactionWrapper(async mng =>
mng.getRepository(entity).findOne(options),
)(),
remove: (entities: T[], options?: RemoveOptions) =>
tenantTransactionWrapper(mng =>
mng.getRepository(entity).remove(entities, options),
)(),
createQueryBuilder: () => {
throw new Error(
'Cannot create queryBuilder for that repository type, instead use: tenantWrapper',
);
},
tenantTransactionWrapper,
});
And finally use our customRepository :
class PersonsRepository implements IPersonsRepository {
private ormRepository: Repository<Person>;
constructor() {
this.ormRepository = AppDataSource.getRepository<Person>(Person).extend(
customRepository(Person),
);
}
public async create(data: ICreatePersonDTO): Promise<Person> {
const newPerson = this.ormRepository.create(data);
await this.ormRepository.save(newPerson);
return newPerson;
}
public async getAll(relations: string[] = []): Promise<Person[]> {
return this.ormRepository.find({ relations });
}
I hope this may help someone and will be very glad if someone provides a better solution.
First you have to create a custom class for wrapping your userId or any stuff
custome_service.ts ==>
#Injectable()
export class UserIdWrapper {
constructor(private dataSource: DataSource) {}
userIdWrapper = (callback: (mng: QueryRunner) => Promise<any>, userId: string) => {
const queryRunner = this.dataSource.createQueryRunner();
return new Promise(async (resolve, reject) => {
let res: any;
try {
await queryRunner.connect();
await queryRunner.manager.query(
`SELECT set_config('your_variable_name', '${userId}', false)`,
);
//here is your funciton your calling in the service
res = await callback(queryRunner);
} catch (err) {
reject(err);
} finally {
await queryRunner.manager.query(`RESET your_variable_name`);
await queryRunner.release();
resolve(res);
}
});
};
}
Now here you have to call the function inside user service
user.service.ts ==>
import { Injectable } from '#nestjs/common';
import { InjectRepository } from '#nestjs/typeorm';
import { Users } from 'src/common/entities';
import { UserIdWrapper } from 'src/common/local-settup/userId_wrapper';
import { DataSource, EntityManager, QueryRunner, Repository } from 'typeorm';
#Injectable()
export class UsersService {
constructor(
#InjectRepository(Users) private userRepository: Repository<Users>,
private dataSource: DataSource,
private userIdWrapper: UserIdWrapper
) {}
async getAllUsers(userId: string) {
//This is your call back funciton that have to pass
const findOne = async (queryRunner: QueryRunner) => {
const res = await queryRunner.query('SELECT * FROM public.users');
return res;
};
try {
//hear we are passing the function in to the class funciton
return this.userIdWrapper.userIdWrapper(findOne, userId);
} catch (err) {
console.log(err);
}
}
}
Dont forgot to provide the custom class service inside the provider of user service.

Unable to close mongoose connection

I am unable to close mongoose connection when I am storing mongoose.connection in a variable called db and creating a separate method to closing the connection via variable db I have tried both method db.close & mongoose.disconnect()
const mongoose = require('mongoose');
var db;
const connectToDB = function (callback) {
let dbUrl;
if (environmentTokens.enviroment === "test") {
dbUrl = localDBUrl;
} else {
dbUrl = environmentTokens.mongoDBUrl;
}
mongoose.connect(dbUrl);
db = mongoose.connection
db.on('error', (err) => {
tracer.error('Connection error with database', err);
})
db.on('connected', () => {
tracer.info('Connected with database', dbUrl);
console.log('Mongoose default connection connected');
callback();
})
// db.on('disconnected', function () {
// console.log('Mongoose default connection disconnected');
// });
// mongoose.connection.close(function () {
// console.log('Mongoose default connection disconnected through app termination')})
};
const getDB = function () {
return db;
};
const disconnectDB = function () {
db.close(function () {
console.log('Mongoose default connection disconnected through app termination')})
// mongoose.disconnect(function () {
// console.log('Mongoose default connection disconnected through app termination')})
}
module.exports = {
connectToDB, disconnectDB, getDB
};
And calling the disconnectDB method from index.js file
process.on('SIGINT', () => {
disconnectDB();
process.exit()
})
I tried to close the connection using following method
mongoose.disconnect(function () {
console.log('Mongoose default connection disconnected');
});
mongoose.connection.close(function () {
console.log('Mongoose default connection disconnected through app termination')})
within disconnectDB method but non of them is closing the connection but when i am using these method inside connectToDB then i am able to close the connection. DOn't understand why this is behaving like this

Cache MongoDb connection with Next.js 10 TypeScript Project - API Route

I'm trying to convert next.js/examples/with-mongodb/util/mongodb.js to TS so I can cache and resue my connections to Mongo within a TS next.js project. I'm getting a TS error on cache.promise that says:
Type 'Promise<MongoClient | { client: MongoClient; db: Db; }>' is not assignable to type 'Promise<MongoClient>'
How should I properly declare the mongo property on global to appease the TS gods?
import { MongoClient, Db } from "mongodb";
const { DATABASE_URL, DATABASE_NAME } = process.env;
declare global {
namespace NodeJS {
interface Global {
mongo: {
conn: MongoClient | null;
promise: Promise<MongoClient> | null;
};
}
}
}
let cached = global.mongo;
if (!cached) {
cached = global.mongo = { conn: null, promise: null };
}
async function connect() {
if (cached.conn) {
return cached.conn;
}
if (!cached.promise) {
const opts = {
useNewUrlParser: true,
useUnifiedTopology: true,
};
cached.promise = MongoClient.connect(DATABASE_URL, opts).then((client) => {
return {
client,
db: client.db(DATABASE_NAME),
};
});
}
cached.conn = await cached.promise;
return cached.conn;
}
export { connect };
You don't need to cache your connection, check latest nextjs with mongodb example. The official mongodb forum experts have navigated me to this example project.
Try to use native solutions
The 'conn' property you are storing contains both MongoClient and Db.
In your global declaration for mongo, you have only included MongoClient. I have the exact same code in my project and the way I handle this is to simply create a basic type called MongoConnection which contains both. Code below.
type MongoConnection = {
client: MongoClient;
db: Db;
};
declare global {
namespace NodeJS {
interface Global {
mongo: {
conn: MongoConnection | null;
promise: Promise<MongoConnection> | null;
}
}
}
}
seems like the answer is to just make the mongo property an any like this:
declare global {
namespace NodeJS {
interface Global {
mongo: any;
}
}
}

MongoDB Stitch and Angular 6 application

I'm creating a service for my MongoDB Stitch connections and I'm having an issue where if I refresh my page I get an error saying:
client for app 'xyxyxyxyxyxy' has not yet been initialized
And when I try to initialize it I get an error saying it has already been initialized.
client for app 'xyxyxyxyxyxy' has already been initialized
Here is my service.
import { Injectable } from '#angular/core';
import { Stitch, RemoteMongoClient, UserApiKeyCredential} from 'mongodb-stitch-browser-sdk';
#Injectable({
providedIn: 'root'
})
export class AnalyticsService {
client: any;
credential: UserApiKeyCredential;
db: any;
constructor() {
console.log(Stitch.hasAppClient('xyxyxyxyxyxy'));
if (!Stitch.hasAppClient('xyxyxyxyxyxy')) {
this.client = Stitch.initializeDefaultAppClient('xyxyxyxyxyxy');
} else {
console.log('here');
this.client = Stitch.initializeAppClient('xyxyxyxyxyxy');
//this.client = Stitch.getAppClient('xyxyxyxyxyxy');
}
this.db = this.client.getServiceClient(RemoteMongoClient.factory, 'mongodb-atlas').db('DBNAME');
}
login() {
this.credential = new UserApiKeyCredential('APIKEY');
this.client.auth.loginWithCredential(this.credential)
.then(authId => {
console.log(authId);
});
}
logout() {
this.client.auth.logout()
.then(resp => {
console.log(resp);
});
}
insertData(collectionName: string, data: {}) {
this.db.collection(collectionName).insertOne(data)
.then(resp => {
console.log(resp);
});
}
getData(collectionName: string) {
this.db.collection(collectionName).find({})
.asArray().then(resp => {
console.log(resp);
});
}
}
Change the constructor to be like this and it fix the issue.
constructor() {
if (!Stitch.hasAppClient('xyxyxyxyxyxy')) {
this.client = Stitch.initializeDefaultAppClient('xyxyxyxyxyxy');
} else {
this.client = Stitch.defaultAppClient;
}
this.db = this.client.getServiceClient(RemoteMongoClient.factory, 'mongodb-atlas').db('DBNAME');
}

Error in connecting database (mongo and nodejs)

I want build a class for wrapping database connection. This is my code ('db.js' file):
var mongodb = require('mongodb');
var Class = function() {
this.db = null;
var server = new mongodb.Server('127.0.0.1', 27017, {auto_reconnect: true});
db = new mongodb.Db('myDB', server);
db.open(function(error, db) {
if (error) {
console.log('Error ' + error);
} else {
console.log('Connected to db.');
this.db = db;
}
});
};
module.exports = Class;
Class.prototype = {
getCollection: function(coll_name) {
this.db.collection(coll_name, function(error, c){ // <--- see error below
return c;
});
}
}
exports.oid = mongodb.ObjectID;
Then, my test code ('test.js' file):
var DB = require('./db');
var myDB = new DB();
myDB.getCollection('myCollection'); // <--- error: Cannot call method 'collection' of null
You are missing "this" in front of "db". eg:
this.db = new mongodb.Db('myDB', server);
And the line next to it.