Created
March 13, 2024 18:57
-
-
Save fzn0x/82caf80c672421fc9a850e427d0375b2 to your computer and use it in GitHub Desktop.
My best practice for MySQL wrapper with App Engine support.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const mysql = require("mysql"); | |
const util = require("util"); | |
const dayjs = require("dayjs"); | |
const sqlString = require("sqlstring"); | |
let config = { | |
host: process.env.DB_HOST, | |
user: process.env.DB_USER, | |
port: process.env.DB_PORT || 3306, | |
database: process.env.DB_DATABASE, | |
password: process.env.DB_PASS, | |
}; | |
// Database Connection for Production (App Engine) | |
if ( | |
process.env.DB_INSTANCE_CONNECTION_NAME && | |
(process.env.NODE_ENV.toLowerCase() === "production" || | |
process.env.NODE_ENV.toLowerCase() === "prod") | |
) { | |
config.socketPath = `/cloudsql/${process.env.DB_INSTANCE_CONNECTION_NAME}`; | |
} | |
// Database Connection for Development | |
let pool = mysql.createPool(config); | |
// use async/await instead of callbacks | |
pool.query = util.promisify(pool.query).bind(pool); | |
pool.getConnection(function (err, connection) { | |
if (err) { | |
console.error("Error connecting: " + err.stack); | |
return; | |
} | |
console.log("Connected as thread id: " + connection.threadId); | |
}); | |
const formatValue = (value) => { | |
if (Object.prototype.toString.call(value) === "[object Date]") { | |
return dayjs(value).format("YYYY-MM-DD HH:mm:ss"); | |
} | |
return value || null; | |
}; | |
// return [ success status, result / errors ] | |
pool.insert = async (tableName, payload = {}) => { | |
try { | |
if (Object.keys(payload).length === 0) { | |
throw new Error( | |
"Payload argument for insert(tableName = '', payload = {}) function cannot be empty!" | |
); | |
} | |
const _insertedValues = Object.values(payload); | |
const generatedQuery = pool.format( | |
`INSERT INTO ${tableName} (${Object.keys(payload) | |
.map((value) => "`" + value + "`") | |
.join(",")}) VALUES (${new Array(_insertedValues.length) | |
.fill("") | |
.map(() => `?`) | |
.join(",")})`, | |
_insertedValues.map((value) => formatValue(value)) | |
); | |
console.log("Executing SQL query: ", generatedQuery); | |
const result = await pool.query(generatedQuery); | |
return [null, result]; | |
} catch (err) { | |
return [err, null]; | |
} | |
}; | |
// return [ Error, result ] | |
pool.update = async (tableName, where = {}, payload = {}) => { | |
try { | |
if (Object.keys(payload).length === 0) { | |
throw new Error( | |
"Payload argument for insert(tableName = '', payload = {}) function cannot be empty!" | |
); | |
} | |
const whereEntries = Object.entries(where); | |
const _updatedValues = Object.values(payload); | |
const generatedQuery = pool.format( | |
`UPDATE ${tableName} SET ${Object.keys(payload) | |
.map((value) => "`" + value + "`") | |
.join(" = ?,")} = ? WHERE ${whereEntries | |
.map((value) => `${value[0]} = ?`) | |
.join(",")}`, | |
[ | |
..._updatedValues.map((value) => formatValue(value)), | |
...whereEntries.map((values) => formatValue(values[1])), | |
] | |
); | |
console.log("Executing SQL query: ", generatedQuery); | |
const result = await pool.query(generatedQuery); | |
return [null, result]; | |
} catch (err) { | |
return [err, null]; | |
} | |
}; | |
pool.select = async (tableName, where = {}) => { | |
try { | |
const whereEntries = Object.entries(where); | |
const generatedQuery = pool.format( | |
`SELECT * FROM ${tableName} WHERE ${whereEntries | |
.map((value) => `${value[0]} = ?`) | |
.join(",")}`, | |
whereEntries.map((value) => formatValue(value[0])) | |
); | |
console.log("Executing SQL query: ", generatedQuery); | |
const result = await pool.query(generatedQuery); | |
return [null, result]; | |
} catch (err) { | |
return [err, null]; | |
} | |
}; | |
pool.insertOrUpdate = async (tableName, where = {}, payload = {}) => { | |
try { | |
if (Object.keys(payload).length === 0) { | |
throw new Error( | |
"Payload argument for insert(tableName = '', payload = {}) function cannot be empty!" | |
); | |
} | |
const result = await pool.select(tableName, where); | |
if (result.length) { | |
const [err, updated] = await pool.update(tableName, where, payload); | |
if (err) throw err; | |
return [null, updated]; | |
} | |
const [err, inserted] = await pool.insert(tableName, payload); | |
if (err) { | |
throw err; | |
} | |
return [null, inserted]; | |
} catch (err) { | |
return [err, null]; | |
} | |
}; | |
pool.format = function (sql, values) { | |
if (typeof this.config.queryFormat === "function") { | |
return this.config.queryFormat.call( | |
this, | |
sql, | |
values, | |
this.config.timezone | |
); | |
} | |
return sqlString.format( | |
sql, | |
values, | |
this.config.stringifyObjects, | |
this.config.timezone | |
); | |
}; | |
module.exports = pool; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment