Skip to content

Instantly share code, notes, and snippets.

@fzn0x
Created March 13, 2024 18:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fzn0x/82caf80c672421fc9a850e427d0375b2 to your computer and use it in GitHub Desktop.
Save fzn0x/82caf80c672421fc9a850e427d0375b2 to your computer and use it in GitHub Desktop.
My best practice for MySQL wrapper with App Engine support.
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