I'm trying to use nextjs and sequelize together. It works but I have a problem with each request from the database it opens a connection for DB. I have a Db class and export an instance from this class then use it wherever I need.
import { FindOptions, Model, ModelCtor, Sequelize } from 'sequelize'
import { initModels, Models } from './models';
class Db {
connection: Sequelize | null = null;
constructor() {
this.connection = this.connectDb()
initModels(this.connection)
}
retrieveAll(model: ModelCtor<Model<any, any>>, options: FindOptions<any> = {}) {
return model?.findAll(options);
}
retrieve(model: ModelCtor<Model<any, any>>, options: FindOptions<any> = {}) {
return model?.findOne(options);
}
connectDb() {
const dbName = process.env.DB_NAME
const dbHost = process.env.DB_HOST
const dbUser = process.env.DB_USER as string
const dbPassword = process.env.DB_PASSWORD
const sequelize = new Sequelize({
dialect: 'postgres',
host: dbHost,
port: 5432,
database: dbName,
username: dbUser,
password: dbPassword,
pool: {
max: 5,
idle: 86400000,
}
});
try {
sequelize.authenticate();
console.log('Connection has been established successfully.');
return sequelize;
} catch (error) {
console.error('Unable to connect to the database:', error);
return null;
}
}
closeConnectionDb() {
this.connection?.close()
}
}
export const db = new Db();
For example, I make a query database for login then on homepage I get merchant list from the database. I see two times "Connection has been established successfully.". How can I solve this problem? By the way, it works very well in the production server. I think it's about module caching but I'm not sure.
Related
I was trying to connect Redis (v4.0.1) to my express server with typescript but having a bit issue. Am learning typescript. It's showing redlines on host inside redis.createClient() Can anyone help me out?
const host = process.env.REDIS_HOST;
const port = process.env.REDIS_PORT;
const redisClient = redis.createClient({
host,
port,
});
Argument of type '{ host: string | undefined; port: string | undefined; }' is not assignable to parameter of type 'Omit<RedisClientOptions<never, RedisScripts>, "modules">'.
Object literal may only specify known properties, and 'host' does not exist in type 'Omit<RedisClientOptions<never, RedisScripts>, "modules">'.ts(2345)
Options have changed when redis updated to 4.0.1. This should help you.
This works as expected (redis v4.1.0)
const url = process.env.REDIS_URL || 'redis://localhost:6379';
const redisClient = redis.createClient({
url
});
what I did in my project was this
file: services/internal/cache.ts
/* eslint-disable no-inline-comments */
import type { RedisClientType } from 'redis'
import { createClient } from 'redis'
import { config } from '#app/config'
import { logger } from '#app/utils/logger'
let redisClient: RedisClientType
let isReady: boolean
const cacheOptions = {
url: config.redis.tlsFlag ? config.redis.urlTls : config.redis.url,
}
if (config.redis.tlsFlag) {
Object.assign(cacheOptions, {
socket: {
// keepAlive: 300, // 5 minutes DEFAULT
tls: false,
},
})
}
async function getCache(): Promise<RedisClientType> {
if (!isReady) {
redisClient = createClient({
...cacheOptions,
})
redisClient.on('error', err => logger.error(`Redis Error: ${err}`))
redisClient.on('connect', () => logger.info('Redis connected'))
redisClient.on('reconnecting', () => logger.info('Redis reconnecting'))
redisClient.on('ready', () => {
isReady = true
logger.info('Redis ready!')
})
await redisClient.connect()
}
return redisClient
}
getCache().then(connection => {
redisClient = connection
}).catch(err => {
// eslint-disable-next-line #typescript-eslint/no-unsafe-assignment
logger.error({ err }, 'Failed to connect to Redis')
})
export {
getCache,
}
then you just import where you need:
import { getCache } from '#services/internal/cache'
const cache = await getCache()
cache.setEx(accountId, 60, JSON.stringify(account))
The option to add a host, port in redis.createClient is no longer supported by redis. So it is not inside type createClient. use URL instead.
import { createClient } from 'redis';
const client = createClient({
socket: {
host: process.env.REDIS_HOST,
port: parseInt(process.env.REDIS_PORT)
},
password: process.env.REDIS_PW
});
client.on('error', (err) => console.error(err));
client.connect();
export { client };
I am new to Nodejs, and trying to create a SQL helper, without success. What am I doing wrong?
db.ts:
import sql from 'mssql/msnodesqlv8'
import dotenv from 'dotenv';
dotenv.config();
//import { config } from 'winston';
const config:sql.config = {
user:process.env.SQL_USER,
password: process.env.SQL_PASSWORD,
database: process.env.SQL_DBNAME,
server: process.env.SQL_URL!,
port: +process.env.SQL_PORT!,
options: {
encrypt: false,
},
}
const poolPromise = new sql.ConnectionPool(config)
.connect()
.then(pool => {
console.log('Connected to MSSQL')
return pool
})
.catch(err => console.log('Database Connection Failed! ', err))
export{ sql, poolPromise }
user.ts
class UserDB {
async login(username:string,password:string){
try {
const pool = await poolPromise
const result = await pool.request()<--- error
.input('username', sql.NVarChar, username)
.input('password', sql.NVarChar, password)
.execute('pwr_loginTenant');
return result.recordsets;
} catch (error) {
console.log('services:user.ts login error',error);
}
}
}
const userDB = new UserDB()
module.exports = userDB;
I am getting error on request:
NODE JS - property 'request' does not exists on type 'void'
typescript connection to mongo database throws error , it can read conn of undefined after conn has been declared globally
UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'conn' of undefined
at Object.connectToDatabase [as default]
import { MongoClient, Db } from "mongodb";
import config from "../config/config";
const { dbName, mongoDBUri } = config;
type MongoConnection = {
client: MongoClient;
db: Db;
};
declare global {
namespace NodeJS {
interface Global {
mongodb: {
conn: MongoConnection | null;
promise: Promise<MongoConnection> | null;
};
}
}
}
let cached = global.mongodb;
async function connectToDatabase() {
if (cached.conn) {
return cached.conn;
}
if (!cached.promise) {
const opts = {
useNewUrlParser: true,
useUnifiedTopology: true,
};
cached.promise = MongoClient.connect(mongoDBUri as string, opts).then(
(client) => {
return {
client,
db: client.db(dbName),
};
}
);
}
cached.conn = await cached.promise;
return cached.conn;
}
export default connectToDatabase;
You can use the below setup
//interfaces/db.interface
export interface dbConfig {
host: string;
port: number;
database: string;
username: string;
password: string;
}
//database.ts
import { dbConfig } from "#interfaces/db.interface";
const { host, port, database, username, password }: dbConfig = config.get("dbConfig");
export const dbConnection = {
url: `mongodb://${username}:${password}#${host}:${port}/${database}?authSource=admin`,
options: {
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false,
useCreateIndex: true
},
};
//app.ts (express app)
import { dbConnection } from "#databases";
constructor(routes: Routes[]) {
this.app = express();
this.port = process.env.PORT || 5000;
this.env = process.env.NODE_ENV || "development";
this.connectToDatabase();
}
private connectToDatabase() {
if (this.env !== "production") {
set("debug", true);
}
connect(dbConnection.url, dbConnection.options)
.catch((error) =>
console.log(`${error}`)
);
}
Here I am assuming you have the setup of paths in the tsconfig.json file so that # will work in imports.
After several times of trying, I had to use the NextJs MongoDB connection pattern and convert it to typescript and it worked perfectly fine
import config from "./../config/config";
import { MongoClient, Db } from "mongodb";
const { dbName, mongoDBUri } = config;
if (!mongoDBUri) {
throw new Error(
"Define the mongoDBUri environment variable inside .env"
);
}
if (!dbName) {
throw new Error(
"Define the dbName environment variable inside .env"
);
}
type MongoConnection = {
client: MongoClient;
db: Db;
};
declare global {
namespace NodeJS {
interface Global {
mongodb: {
conn: MongoConnection | null;
promise: Promise<MongoConnection> | null;
};
}
}
}
let cached = global.mongodb;
if (!cached) {
cached = global.mongodb = { conn: null, promise: null };
}
export default async function connectToDatabase() {
if (cached.conn) {
return cached.conn;
}
if (!cached.promise) {
console.log("Establishing new database connection");
const opts = {
useNewUrlParser: true,
useUnifiedTopology: true,
};
cached.promise = MongoClient.connect(mongoDBUri as string, opts).then(
(client) => {
return {
client,
db: client.db(dbName),
};
}
);
}
cached.conn = await cached.promise;
return cached.conn;
}
What's the best way to connect to MongoDB with typescript that reuses the existing connection. I recently migrated my JavaScript codebase to typescript and the MongoDB connection in typescript throws an error
Please I need how to connect with MongoDB driver with typescript in such a way that I can reuse the existing database connection or help me fix my connection problem
Here is the error from the code below (node:3732) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'conn' of undefined
import { MongoClient, Db } from "mongodb";
const { DATABASE_URL, DATABASE_NAME } = process.env;
type MongoConnection = {
client: MongoClient;
db: Db;
};
declare global {
namespace NodeJS {
interface Global {
mongodb: {
conn: MongoConnection | null;
promise: Promise<MongoConnection> | null;
};
}
}
}
let cached = global.mongodb;
async function connectToDatabase() {
if (cached.conn) {
return cached.conn;
}
if (!cached.promise) {
const opts = {
useNewUrlParser: true,
useUnifiedTopology: true,
};
cached.promise = MongoClient.connect(DATABASE_URL as string, opts).then(
(client) => {
return {
client,
db: client.db(DATABASE_NAME),
};
}
);
}
cached.conn = await cached.promise;
return cached.conn;
}
export { connectToDatabase };
Try this
After your statement let cached = global.mongodb; add these lines
if (!cached) {
cached = global.mongodb = { conn: null, promise: null };
}
So it should look like this
import { MongoClient, Db } from "mongodb";
const { DATABASE_URL, DATABASE_NAME } = process.env;
type MongoConnection = {
client: MongoClient;
db: Db;
};
declare global {
namespace NodeJS {
interface Global {
mongodb: {
conn: MongoConnection | null;
promise: Promise<MongoConnection> | null;
};
}
}
}
let cached = global.mongodb;
if (!cached) {
cached = global.mongodb = { conn: null, promise: null };
}
async function connectToDatabase() {
if (cached.conn) {
return cached.conn;
}
if (!cached.promise) {
const opts = {
useNewUrlParser: true,
useUnifiedTopology: true,
};
cached.promise = MongoClient.connect(DATABASE_URL as string, opts).then(
(client) => {
return {
client,
db: client.db(DATABASE_NAME),
};
}
);
}
cached.conn = await cached.promise;
return cached.conn;
}
export { connectToDatabase };
For the reasoning, you have extended the Global interface to include the conn and promise to mongodb but you forgot to initialize the cached variable with that object.
After setting cached to { conn: null, promise: null } you have set their initial value to be null.
And the handy if check before setting them to null is to make sure you don't set them to null all the time, I am guessing this is being hosted in a serverless environment where you want to reuse the connection.
Below is my code (under file db.js)
function connection() {
try {
const mysql = require('mysql2');
const pool = mysql.createPool({
host: "mydb.abcd1234.ap-southeast-1.rds.amazonaws.com",
database: "mydatabase",
user: "user",
password: "password",
connectionLimit: 11,
waitForConnections: true,
queueLimit: 0,
namedPlaceholders: true
});
const promisePool = pool.promise();
return promisePool;
} catch (error) {
return console.log(`Could not connect - ${error}`);
}
}
const pool = connection();
module.exports = {
connection: async () => pool.getConnection(),
execute: (...params) => pool.execute(...params)
};
And to use it, I simply add var db = require('./../db.js'); at the top of the files that requires it. Additionally, below is how I'd execute any sql statements
const myResult = await db.execute("Select COUNT(*) AS dataCount from teachers WHERE username = ?", [username]);
But now I need to separate my database according to countries. So my database would be named with mydatabase_ + the country code, for example mydatabase_my , mydatabase_jp, mydatabase_uk etc etc. So how do I pass the country code to the function connection() ?
I tried something like
function connection(country) {
try {
const mysql = require('mysql2');
const pool = mysql.createPool({
host: "mydb.abcd1234.ap-southeast-1.rds.amazonaws.com",
database: "mydatabase_" + country,
user: "user",
password: "password",
connectionLimit: 11,
waitForConnections: true,
queueLimit: 0,
namedPlaceholders: true
});
const promisePool = pool.promise();
return promisePool;
} catch (error) {
return console.log(`Could not connect - ${error}`);
}
}
const pool = connection(country);
module.exports = {
connection: async (country) => pool.getConnection(country),
execute: (...params) => pool.execute(...params)
};
and at the calling page, I did
var db = require('./../db.js');
db.connection("my")
but for the above, I will get ReferenceError: country is not defined which refers to country at the line const pool = connection(country);
Here's how I did it:
const mysql = require('mysql2');
const pool = function(country){
var mydb = mysql.createPool({
host: "mydb.abcd1234.ap-southeast-1.rds.amazonaws.com",
database: "mydatabase_" + country,
user: "user",
password: "password",
connectionLimit: 11,
waitForConnections: true,
queueLimit: 0,
namedPlaceholders: true
});
return mydb;
}
const promisePool = function(country){
return pool(country).promise();
}
module.exports = {
connection: async (country) => promisePool(country).getConnection(),
execute: (country,...params) => promisePool(country).execute(...params)
};
And in the page that uses mysql will have something like this:
db.connection(country)
const myResult = await db.execute(country, "Select * from schools");