promisesql
Version:
A [node-sqlite3](https://www.npmjs.com/package/sqlite3) wrapper for running simple, promise-based database queries in Node.js. It works best in smaller projects with a lot of asynchronous development, e.g., a Discord bot that implements slash commands.
180 lines (156 loc) • 4.75 kB
JavaScript
const { PromiseDB } = require('./promisedb');
const { accessError, asyncError } = require('../util/psql-error');
const { simplifyArray, simplifyObject } = require('../util/helpers');
const str = require('../util/string');
const sp = require('synchronized-promise');
// Current database (undefined if not open)
var db = undefined;
var simplify = false;
/**
* Opens a database file.
* @param {string} file Relative path to the database file
* @returns {void|never}
*/
function open(file) {
if (db instanceof PromiseDB) {
close();
accessError(true);
}
db = new PromiseDB(file);
}
/**
* Closes an open database.
* @returns {void|never}
*/
function close() {
db instanceof PromiseDB ? db.close() : accessError();
db = undefined;
}
/**
* Retrieves the database, if open.
* @returns {PromiseDB|never}
*/
function get() {
if (db instanceof PromiseDB) return db;
accessError();
}
/**
* Dynamically opens and closes a database if a file is provided.
* @param {string} file Relative path to the database file
* @returns {void|Promise<void>}
*/
function dynamicAccess(file) {
// If file is undefined or db is already open, do nothing
if (!file || db instanceof PromiseDB) {
return;
}
// Open db and then close it when promise is fulfilled
return new Promise((resolve, reject) => {
try {
open(file);
resolve({ then: close });
} catch (error) {
reject(error);
}
});
}
/* ================ QUERIES ================ */
class Query {
/**
* Query constructor - handles dynamic database access
* @param {BaseOptions} options
*/
constructor(options) {
this.options = options;
dynamicAccess(options.file);
}
/**
* Asynchronous database query.
* @returns {QueryPromise}
*/
async run() {
return await db.query(this.options.statement, this.options.args);
}
/**
* Asynchronous instert query.
* @returns {Promise<void>}
*/
async insert() {
const sql = str.insertStr(this.options);
await db.query(sql, this.options.values);
}
/**
* Asynchronous selection query.
* @returns {SelectionPromise}
*/
async select() {
const { sql, args } = str.selectStr(this.options);
const data = await db.query(sql, args);
return (data.length > 0 && this.options.first) ? data[0] : data;
}
/**
* Asynchronous update query.
* @returns {Promise<void>}
*/
async update() {
const { sql, args } = str.updateStr(this.options);
await db.query(sql, args);
}
/**
* Asynchronous delete query.
* @returns {Promise<void>}
*/
async remove() {
const { sql, args } = str.deleteStr(this.options);
await db.query(sql, args);
}
/**
* Asynchronous upsert query.
* @returns {Promise<void>}
*/
async upsert() {
await this.insert(this.options).catch(async error => {
if (error.code !== 'SQLITE_CONSTRAINT') throw new error;
if (!this.options.table) this.options.table = this.options.into;
await this.update(this.options);
});
}
}
/**
* Synchronous query.
* @param {Function} query
* @param {string[]|BaseOptions} options
* @returns {QueryRetval}
*/
function sync(query, options = []) {
if (query.constructor.name !== 'AsyncFunction') asyncError();
return sp(query)(options);
}
const queryNames = Object.getOwnPropertyNames(Query.prototype).filter(name => name !== 'constructor'), queries = new Object();
queryNames.forEach(name => {
queries[name] = async options => {
const query = new Query(options);
const run = query[name]();
if (simplify) run.then(data => Array.isArray(data) ? simplifyArray(data) : simplifyObject(data));
return await run;
}
});
module.exports = {
simplifyOutput: (bit = undefined) => typeof bit === 'boolean' ? simplify = bit : simplify = !simplify,
// Export database open/close functions (synchronous!)
open, close, get,
// Queries
...queries,
/**
* @param {SelectionOptions} options
* @returns
*/
select: async options => new Query(options).select(),
sync,
/**
* Asynchronous delete query. Optional overload, since 'delete' is reserved keyword.
* @param {DeleteOptions} options
* @returns {Promise<void>}
*/
delete: async options => await queries.remove(options)
}