UNPKG

@tonyism1/db-utils

Version:

MySQL utilities with automatic table and column creation, plus B2 Backblaze media storage

137 lines (119 loc) 4.93 kB
import mysql from "mysql2/promise"; import { detectType, mapKeysToSnakeCase, toSnakeCase } from "./utils.js"; export async function createDB(config) { const pool = mysql.createPool(config); // Ensure table exists; create with minimal structure if missing async function ensureTable(table) { const [rows] = await pool.query("SHOW TABLES LIKE ?", [table]); if (rows.length === 0) { console.log(`Creating table '${table}'`); await pool.query(`CREATE TABLE \`${table}\` (id INT AUTO_INCREMENT PRIMARY KEY)`); } } // Ensure all columns exist; create missing ones async function ensureColumns(table, data) { await ensureTable(table); const columnsQuery = await pool.query(`SHOW COLUMNS FROM \`${table}\``); const existingColumns = columnsQuery[0].map(r => r.Field); const snakeData = mapKeysToSnakeCase(data); for (const key of Object.keys(snakeData)) { if (!existingColumns.includes(key)) { const type = detectType(snakeData[key]); console.log(`Adding column '${key}' to table '${table}' as ${type}`); await pool.query( `ALTER TABLE \`${table}\` ADD COLUMN \`${key}\` ${type} DEFAULT NULL` ); } } return snakeData; } return { pool, async insert(table, data) { const snakeData = await ensureColumns(table, data); const keys = Object.keys(snakeData); const values = Object.values(snakeData); const placeholders = keys.map(() => "?").join(","); const [result] = await pool.query( `INSERT INTO \`${table}\` (${keys.join(",")}) VALUES (${placeholders})`, values ); return result.insertId; }, async update(table, data, where) { const snakeData = await ensureColumns(table, data); const set = Object.keys(snakeData).map(k => `\`${k}\` = ?`).join(", "); const whereKeys = mapKeysToSnakeCase(where); const whereClause = Object.keys(whereKeys).map(k => `\`${k}\` = ?`).join(" AND "); const values = [...Object.values(snakeData), ...Object.values(whereKeys)]; await pool.query(`UPDATE \`${table}\` SET ${set} WHERE ${whereClause}`, values); }, async findOne(table, where) { const whereKeys = mapKeysToSnakeCase(where); const whereClause = Object.keys(whereKeys).map(k => `\`${k}\` = ?`).join(" AND "); const values = Object.values(whereKeys); const [rows] = await pool.query(`SELECT * FROM \`${table}\` WHERE ${whereClause} LIMIT 1`, values); return rows[0] || null; }, async findAll(table, where = {}, options = {}) { const whereKeys = mapKeysToSnakeCase(where); const whereClause = Object.keys(whereKeys).length ? "WHERE " + Object.keys(whereKeys).map(k => `\`${k}\` = ?`).join(" AND ") : ""; const values = Object.values(whereKeys); let sql = `SELECT * FROM \`${table}\` ${whereClause}`; if (options.limit) sql += ` LIMIT ${options.limit}`; const [rows] = await pool.query(sql, values); return rows; }, async delete(table, where = {}) { const whereKeys = mapKeysToSnakeCase(where); const whereClause = Object.keys(whereKeys).length ? "WHERE " + Object.keys(whereKeys).map(k => `\`${k}\` = ?`).join(" AND ") : ""; const values = Object.values(whereKeys); const sql = `DELETE FROM \`${table}\` ${whereClause}`; await pool.query(sql, values); }, async createTable(table, columns = {}) { // Create table with specified columns or just id column if none provided let columnDefinitions = "id INT AUTO_INCREMENT PRIMARY KEY"; // Add additional columns if provided for (const [key, type] of Object.entries(columns)) { const snakeKey = toSnakeCase(key); columnDefinitions += `, \`${snakeKey}\` ${type}`; } const sql = `CREATE TABLE IF NOT EXISTS \`${table}\` (${columnDefinitions})`; await pool.query(sql); }, async dropTable(table) { const sql = `DROP TABLE IF EXISTS \`${table}\``; await pool.query(sql); }, async addColumn(table, column, type) { const snakeColumn = toSnakeCase(column); const sql = `ALTER TABLE \`${table}\` ADD COLUMN \`${snakeColumn}\` ${type} DEFAULT NULL`; await pool.query(sql); }, async getTables() { const [rows] = await pool.query("SHOW TABLES"); // Extract table names from the result return rows.map(row => Object.values(row)[0]); }, async getTableColumns(table) { const [rows] = await pool.query(`SHOW COLUMNS FROM \`${table}\``); return rows.map(row => ({ field: row.Field, type: row.Type, null: row.Null, key: row.Key, default: row.Default, extra: row.Extra })); }, async query(sql, params = []) { const [rows] = await pool.query(sql, params); return rows; } }; }