@pardnchiu/mysql-pool
Version:
A MySQL connection pool solution designed specifically for Node.js, featuring intuitive chaining syntax that integrates query builder capabilities with read-write separation.
390 lines • 13.3 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
// TODO transform to npm package for other projects easily to use
const promise_1 = require("mysql2/promise");
;
class MySQLPool {
constructor() { }
;
static correctConfig(config) {
return {
host: String(config.host || "localhost").trim(),
port: parseInt(String(config.port).trim()) || 3306,
user: String(config.user || "root").trim(),
password: String(config.password || "").trim(),
database: String(config.database || "").trim(),
charset: (config.charset || "utf8mb4").trim(),
connectionLimit: parseInt(String(config.connectionLimit || 8).trim()) || 8
};
}
static async init(config = {}) {
this.config = config;
let readConfig = this.correctConfig(config.config || config.read || {
host: (process.env.DB_READ_HOST || "localhost").trim(),
port: parseInt(String(process.env.DB_READ_PORT).trim()) || 3306,
user: (process.env.DB_READ_USER || "root").trim(),
password: process.env.DB_READ_PASSWORD || "",
database: (process.env.DB_READ_DATABASE || "").trim(),
charset: (process.env.DB_READ_CHARSET || "utf8mb4").trim(),
connectionLimit: parseInt(String(process.env.DB_READ_CONNECTION).trim()) || 4,
});
let writeConfig = this.correctConfig(config.config || config.write || config.read || {
host: (process.env.DB_WRITE_HOST || "localhost").trim(),
port: parseInt(String(process.env.DB_WRITE_PORT).trim()) || 3306,
user: (process.env.DB_WRITE_USER || "root").trim(),
password: process.env.DB_WRITE_PASSWORD || "",
database: (process.env.DB_WRITE_DATABASE || "").trim(),
charset: (process.env.DB_WRITE_CHARSET || "utf8mb4").trim(),
connectionLimit: parseInt(String(process.env.DB_WRITE_CONNECTION).trim()) || 4,
});
try {
if ((readConfig.database || "").length > 0) {
this.readPool = (0, promise_1.createPool)({
...readConfig,
waitForConnections: true
});
const connection = await this.readPool.getConnection();
connection.release();
}
;
if ((writeConfig.database || "").length > 0) {
this.writePool = (0, promise_1.createPool)({
...writeConfig,
waitForConnections: true
});
const connection = await this.writePool.getConnection();
connection.release();
}
;
}
catch (err) {
console.error("MySQL initialization failed:", err);
throw err;
}
;
}
;
static async close() {
try {
if (this.readPool) {
await this.readPool.end();
this.readPool = null;
}
;
if (this.writePool) {
await this.writePool.end();
this.writePool = null;
}
;
}
catch (err) {
console.error("Failed to close MySQL connections:", err);
throw err;
}
;
}
;
static reset() {
this.tableName = null;
this.whereAry = [];
this.bindingAry = [];
this.orderAry = [];
this.queryLimit = null;
this.queryOffset = null;
this.joinAry = [];
this.selectAry = ["*"];
this.setAry = [];
this.withTotal = false;
this.currentTarget = "read";
}
;
static table(tableName, target = "read") {
this.reset();
this.tableName = tableName;
this.currentTarget = target;
return this;
}
;
static total() {
this.withTotal = true;
return this;
}
;
static select(...fields) {
if (fields.length > 0) {
this.selectAry = fields;
}
;
return this;
}
;
static innerJoin(table, first, operator, second) {
return this.join("INNER", table, first, operator, second);
}
;
static leftJoin(table, first, operator, second) {
return this.join("LEFT", table, first, operator, second);
}
;
static rightJoin(table, first, operator, second) {
return this.join("RIGHT", table, first, operator, second);
}
;
static join(type, table, first, operator, second) {
if (second === undefined) {
second = operator;
operator = "=";
}
;
first = !first.includes(".") ? `\`${first}\`` : first;
second = !second.includes(".") ? `\`${second}\`` : second;
this.joinAry.push(`${type} JOIN \`${table}\` ON ${first} ${operator} ${second}`);
return this;
}
;
static where(column, operator, value) {
if (operator === "LIKE" && typeof value === "string") {
value = `%${value}%`;
}
else if (operator === "IN" && Array.isArray(value)) {
value = value.map(v => v.toString());
}
else if (value == null) {
value = operator;
operator = "=";
}
;
column = !column.includes('(') && !column.includes(".") ? `\`${column}\`` : column;
this.whereAry.push(`${column} ${operator} ${operator === "IN" ? "(?)" : "?"}`);
this.bindingAry.push(value);
return this;
}
;
static orderBy(column, direction = "ASC") {
direction = direction.toUpperCase();
if ({
ASC: 1,
DESC: 1,
asc: 1,
desc: 1
}[direction] == null) {
console.error("Invalid order direction:", direction);
return this;
}
;
column = !column.includes(".") ? `\`${column}\`` : column;
this.orderAry.push(`${column} ${direction}`);
return this;
}
;
static limit(num) {
this.queryLimit = num;
return this;
}
;
static offset(num) {
this.queryOffset = num;
return this;
}
;
static increase(target, number) {
this.setAry.push(`${target} = ${target} + ${number || 1}`);
return this;
}
;
static async get() {
if (this.tableName === null) {
throw new Error("Table not set, query aborted.");
}
;
const fieldName = this.selectAry.map(field => {
field === "*" ? "*" : (!field.includes(".") ? `\`${field}\`` : field);
switch (true) {
case field === "*": return "*";
case /[\.\(\)]+/.test(field): return field;
default: return `\`${field}\``;
}
;
}).join(", ");
let query = `SELECT ${fieldName} FROM \`${this.tableName}\``;
if (this.joinAry.length > 0) {
query += " " + this.joinAry.join(" ");
}
;
if (this.whereAry.length > 0) {
query += " WHERE " + this.whereAry.join(" AND ");
}
;
if (this.withTotal) {
query = `SELECT COUNT(*) OVER() AS total, data.* FROM (${query}) AS data`;
}
;
if (this.orderAry.length > 0) {
query += " ORDER BY " + this.orderAry.join(", ");
}
;
if (this.queryLimit !== null) {
query += ` LIMIT ${this.queryLimit}`;
}
;
if (this.queryOffset !== null) {
query += ` OFFSET ${this.queryOffset}`;
}
;
return await this.query(query, this.bindingAry);
}
;
static async insert(data) {
if (this.tableName === null) {
throw new Error("Table not set, insert aborted.");
}
;
const columns = Object.keys(data);
const values = Object.values(data);
const placeholders = Array(values.length).fill('?');
const query = `INSERT INTO \`${this.tableName}\` (\`${columns.join("`, `")}\`) VALUES (${placeholders.join(", ")})`;
const result = await this.query(query, values, "write");
return result.insertId || null;
}
;
static async update(data = {}) {
if (this.tableName === null) {
throw new Error("Table not set, update aborted.");
}
;
const values = [];
for (let [column, value] of Object.entries(data)) {
column = !column.includes(".") ? `\`${column}\`` : column;
if (typeof value === "string" && this.supportFunction.includes(value.toUpperCase())) {
this.setAry.push(`${column} = ${value}`);
}
else {
this.setAry.push(`${column} = ?`);
values.push(value);
}
;
}
;
let query = `UPDATE \`${this.tableName}\` SET ${this.setAry.join(", ")}`;
if (this.whereAry.length > 0) {
query += " WHERE " + this.whereAry.join(" AND ");
}
;
return await this.query(query, [...values, ...this.bindingAry], "write");
}
;
static async upsert(data, updateData) {
if (this.tableName === null) {
throw new Error("Table not set, upsert aborted.");
}
;
const columns = Object.keys(data);
const values = Object.values(data);
const placeholders = Array(values.length).fill('?');
const updateValues = [];
let updateClause = "";
if (typeof updateData === "string") {
updateClause = ` ON DUPLICATE KEY UPDATE ${updateData}`;
}
else if (updateData && typeof updateData === "object") {
const updateParts = [];
for (let [column, value] of Object.entries(updateData)) {
column = !column.includes(".") ? `\`${column}\`` : column;
if (typeof value === "string" && this.supportFunction.includes(value.toUpperCase())) {
updateParts.push(`${column} = ${value}`);
}
else {
updateParts.push(`${column} = ?`);
updateValues.push(value);
}
}
updateClause = ` ON DUPLICATE KEY UPDATE ${updateParts.join(", ")}`;
}
else {
updateClause = ` ON DUPLICATE KEY UPDATE ` +
columns.map(col => {
const column = !col.includes(".") ? `\`${col}\`` : col;
return `${column} = VALUES(${column})`;
}).join(", ");
}
;
const query = `INSERT INTO \`${this.tableName}\` (\`${columns.join("`, `")}\`) VALUES (${placeholders.join(", ")})${updateClause}`;
const result = await this.query(query, [...values, ...updateValues], "write");
return result.insertId || null;
}
static async query(query, params = [], target) {
const useTarget = target || this.currentTarget;
const pool = useTarget === "write" ? this.writePool : this.readPool;
if (!pool) {
throw new Error(`${useTarget} connection is not available.`);
}
;
const startTime = Date.now();
let connection = null;
try {
connection = await pool.getConnection();
const [results] = await connection.query(query, params);
const endTime = Date.now();
const duration = endTime - startTime;
if (duration > 20) {
console.log(`[Slow Query: ${duration}ms] [${query}]`);
}
;
return results;
}
catch (error) {
throw error;
}
finally {
if (connection)
connection.release();
}
}
;
static async read(query, params = []) {
if (!this.readPool) {
throw new Error(`Read pool connection is not available.`);
}
;
return this.query(query, params, "read");
}
;
static async write(query, params = []) {
if (!this.writePool) {
throw new Error(`Write pool connection is not available.`);
}
;
return this.query(query, params, "write");
}
;
}
MySQLPool.config = {};
MySQLPool.readPool = null;
MySQLPool.writePool = null;
MySQLPool.tableName = null;
MySQLPool.selectAry = ["*"];
MySQLPool.joinAry = [];
MySQLPool.whereAry = [];
MySQLPool.bindingAry = [];
MySQLPool.orderAry = [];
MySQLPool.setAry = [];
MySQLPool.queryLimit = null;
MySQLPool.queryOffset = null;
MySQLPool.withTotal = false;
MySQLPool.currentTarget = "read";
MySQLPool.supportFunction = [
"NOW()", "CURRENT_TIMESTAMP", "UUID()", "RAND()", "CURDATE()",
"CURTIME()", "UNIX_TIMESTAMP()", "UTC_TIMESTAMP()", "SYSDATE()",
"LOCALTIME()", "LOCALTIMESTAMP()", "PI()", "DATABASE()", "USER()",
"VERSION()"
];
process.on("SIGINT", async (_) => {
await MySQLPool.close();
process.exit(0);
});
process.on("SIGTERM", async (_) => {
await MySQLPool.close();
process.exit(0);
});
exports.default = MySQLPool;
//# sourceMappingURL=MySQLPool.js.map