UNPKG

@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
"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