A while ago I was working on a multipurpose database class (an es6 javascript class) that works with any database supporting adodb.
This class is a wrapper on the "node-adodb" npm library. This database class has four main methods, all of which process some options, passed in as parameters, into sql, which is then run on the database.
I am particularly worried about my processing of the options, as I have little experience in that field.
I have been programming for about 2 years or so and am looking for any criticisms/feedback.
Here is my code:
'use strict'
/**
 * @file A file that contains the "dataBase" class.
 */
const debug = require('debug')('Data-comms:dataBase')
const adodb = require('node-adodb')
adodb.debug = true
/**
 * @constructor
 * @param {string} connection - The connection string to the database.
 * @param {boolean} X64  - Whether or not you are using the 64 bit version.
 * @name dataBase
 * @description A class for: Connecting to, Querying, etc, adodb databases.
 */
exports.dataBase = class DataBase {
  constructor(connection, X64) {
    this.connectString = connection
    this.X64 = X64
    this.connection = adodb.open(connection, X64)
    debug('Connection opened:', this.connection)
    this._this = this
    debug('dataBase class initialized:', this)
  }
  /**
   * @async
   * @function dataBase#close
   * @description Closes the connection to the database.
   */
  async close() {
    await this.connection.close()
    debug('connection closed:', this.connection)
    return
  }
  /**
   * @async
   * @function dataBase#reopen
   * @description Reopens the connection to the database if it has been closed.
   */
  async reopen() {
    this.connection = adodb.open(this.connectString, this.X64)
    debug('connection reopened:', this.connection)
    return
  }
  /**
   * @async
   * @function dataBase#runSQL
   * @param {string} SQL - The SQL that will be run.
   * @description Runs any valid SQL on the database.
   * @returns {object[]|object} If the SQL is a query, this is the result.
   */
  async runSQL(sql) {
    debug('starting SQL execution:', sql)
    debug('SQL code execution type:', sql.match('SELECT') ? 'query' : 'execute')
    const data = await this.connection[sql.match('SELECT') ? 'query' : 'execute'](sql)
    debug('SQL ran with result:', data)
    return data
  }
  /**
   * @async
   * @function dataBase#query
   * @param {string} table - The table that you are querying.
   * @param {string[]|string} [columns] - The column(s) you want to query - If left empty or as '*' it will query all columns.
   * @param {string[]|string} [rows] - The ID of the row(s) you want to query - If left empty or as '*' it will query all rows.
   * @param {string[]|string} [options] - Any additional paramiters in the query - If left empty there will be no additional paramiters.
   * @param {Boolean|object} [isUpdate=false] - Whether or not to update the selected fields, if so it is an object containing info about what columns change to what.
   * @description Runs a query based on the four paramiters described below. Here are all of it's child functions.
   * @returns {object[]} The result of the query.
   */
  async query(table, columns = '*' || [], rows = '*' || [], options = '*' || []) {
    debug('starting query with arguments:', 'table:', table, 'columns:', columns, 'rows:', rows, 'options:', options)
    function makeArray(str) {
      return typeof str === 'string' && str !== '*' ? [str] : str
    }
    columns = makeArray(columns)
    rows = makeArray(rows)
    options = makeArray(options)
    function processData(table, columns, rows, options) {
      debug('Starting data processing')
      function processColumns(columns) {
        let retval = ''
        for(const i in columns) {
          if(i != columns.length - 1) {
            retval += `${columns[i]},`
          } else {
            retval += `${columns[i]}`
            return retval
          }
        }
      }
      function processRows(rows) {
        let retval = ''
        for(const i in rows) {
          if(i != rows.length - 1) {
            retval += `ID=${rows[i]} OR `
          } else {
            retval += `ID=${rows[i]}`
          }
        }
        return retval
      }
      function processOptions(options) {
        let retval = ''
        for(const i in rows) {
            retval += ` AND ${options[i]}`
        }
        return retval
      }
      const SQLcolumns = processColumns(columns)
      const SQLrows = processRows(rows)
      const SQLoptions = processOptions(options)
      debug('Finished data processing')
      debug('Running query:', `SELECT ${SQLcolumns} FROM [${table}] ${rows === '*' && options === '*'? '' : 'WHERE'} ${rows === '*' ? '' : SQLrows}${options === '*' ? '' : SQLoptions};`)
      return `SELECT ${SQLcolumns} FROM [${table}] ${rows === '*' && options === '*'? '' : 'WHERE'} ${rows === '*' ? '' : SQLrows}${options === '*' ? '' : SQLoptions};`
    }
    const processed = processData(table, columns, rows, options)
    const data = await this.runSQL(processed)
    debug('Query ran with result:', data)
    return data
  }
  /**
   * @async
   * @function dataBase#createTable
   * @param {string} name - The name of the table that will be made.
   * @param {object} columns - The columns in the table, for each property the key is the column name and the value is the column type.
   * @param {object} [rows] - The rows to initially add to the dataBase, if left blank there will be no inital rows. - In each property the value will be the value inserted into the column, the column is determined by the order of the properties.
   * @description Creates a table based on the peramiters below.
   */
  async createTable(name, columns, rows = null) {
    debug('starting table creation with paramiters:', 'name:', name, 'columns:', columns, 'rows:', rows)
    debug('Starting data processing')
    function processColumns(columns) {
      let retval = ''
      for(const i of Object.keys(columns)) {
        i !== Object.keys(columns)[Object.keys(columns).length - 1] ? retval += `${i} ${columns[i]},\n` : retval += `${i} ${columns[i]}`
      }
      return retval
    }
    debug('Finished data processing')
    const SQLcolumns = processColumns(columns)
    debug('Creating table')
    const data = await this.runSQL(`CREATE TABLE ${name} (\n${SQLcolumns}\n);`)
    debug('Table created with result:', data)
    if(rows !== null) {
      debug('Adding records:', rows)
      await this.addRecords(name, rows)
      debug('Records added')
    }
    return data
  }
  /**
   * @async
   * @function dataBase#addRecords
   * @param {string} table - The name of the the table that the rows will be inserted into.
   * @param {object} values - The rows to add to the dataBase. - In each property the value will be the value inserted into the column, the column is determined by the order of the properties.
   * @description Adds records to a database based on the peramiters below.
   */
  async addRecords(table, values) {
    debug('starting record adding with paramiters:', 'table:', table, 'values:', values)
    debug('Starting data processing')
    const data = []
    function processValues(values) {
      let retval = ''
      for(const i of Object.keys(values)) {
        i !== Object.keys(values)[Object.keys(values).length - 1] ? retval += `${values[i]}, ` : retval += values[i]
      }
      return retval
    }
    debug('Finished data processing')
    for(const i of values) {
      const SQLvalues = processValues(i)
      debug('Inserting:', SQLvalues)
      await this.runSQL(`INSERT INTO [${table}] VALUES (${SQLvalues});`).then((result) => {
        debug('Values inserted with result:', result)
        data.push(result)
      })
    }
    debug('Finished row insertion with result:', data)
    return data
  }
}