{"id":15731882,"url":"https://github.com/stopsopa/knex-abstract","last_synced_at":"2026-01-27T17:38:59.754Z","repository":{"id":46102418,"uuid":"162354569","full_name":"stopsopa/knex-abstract","owner":"stopsopa","description":"📙Abstract on top of knex lib providing managers classes for each tables in db and custom abstract class for all of them","archived":false,"fork":false,"pushed_at":"2024-10-25T09:27:41.000Z","size":2162,"stargazers_count":0,"open_issues_count":3,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-24T02:02:41.632Z","etag":null,"topics":["knex","knexjs","migration","migrations","mysql","nodejs","pdo"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stopsopa.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-12-18T22:53:58.000Z","updated_at":"2024-10-25T09:01:39.000Z","dependencies_parsed_at":"2024-12-12T22:41:17.220Z","dependency_job_id":"8dee68b0-4732-40a7-bab5-9e2fa8679626","html_url":"https://github.com/stopsopa/knex-abstract","commit_stats":{"total_commits":400,"total_committers":6,"mean_commits":66.66666666666667,"dds":0.5375,"last_synced_commit":"5c892e67e6e75f0a9e11d24874569eff7c6d5100"},"previous_names":[],"tags_count":146,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stopsopa%2Fknex-abstract","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stopsopa%2Fknex-abstract/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stopsopa%2Fknex-abstract/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stopsopa%2Fknex-abstract/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stopsopa","download_url":"https://codeload.github.com/stopsopa/knex-abstract/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250546068,"owners_count":21448259,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["knex","knexjs","migration","migrations","mysql","nodejs","pdo"],"created_at":"2024-10-04T00:06:49.543Z","updated_at":"2026-01-27T17:38:59.733Z","avatar_url":"https://github.com/stopsopa.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"![example workflow](https://github.com/stopsopa/knex-abstract/actions/workflows/tests.yml/badge.svg)\n[![npm version](https://badge.fury.io/js/knex-abstract.svg)](https://badge.fury.io/js/knex-abstract)\n\n[![NpmLicense](https://img.shields.io/npm/l/knex-abstract.svg)](https://github.com/knex-abstract/blob/master/LICENSE)\n\n[![knex-abstract youtube demo - nested set example](yt.png)](https://youtu.be/d8k98noOR5c)\n[![dependencies](https://depx.co/api/badge/knex-abstract)](https://depx.co/pkg/knex-abstract)\n\nhttps://github.com/stopsopa/knex-abstract\n\n# Installation:\n\n    npx knex-abstract\n    # follow instruction on the screen\n    cd knex-project\n    cat test.js\n    # and see Makefile\n\n# usege:\n\n    require('dotenv-up')(4, false, '.env');\n\n    const knex              = require('knex-abstract');\n\n    const config      = require('config');\n\n    knex.init(config);\n\n    (async function(){\n\n        const list = await knex().model.common.raw('show tables');\n\n        // ...\n\n\n    })();\n\n# examples:\n\nSee example of [config](models/config.js)\n\nFollow:\n\n- [test cases](https://github.com/stopsopa/knex-abstract/blob/master/test/knex/mysql/mysql-insert.test.js)\n- [test script](https://github.com/stopsopa/knex-abstract/blob/master/example/test.js)\n\n# Api\n\n```javascript\n\nimport knex from 'knex-abstract';\n\nconst { Opt }           = knex.prototype;\n// or import { Opt } from 'knex-abstract';\n\nconst prototype         = knex.prototype;\n\nconst a                 = prototype.a;\n\nconst man = knex().model.registered_manager_name;\n\n(async function () {\n\n    /**\n     * Just helper to process parameters, return raw data as it is returned from native knex\n     */\n    const result = await man.raw(`select * from ...`, {...params});\n\n    /**\n     * The same like .raw() but it extracts proper response data\n     *\n     * NOTE: Uses .raw() internally\n     */\n    const data1 = await man.query(`select * from ...`, {...params});\n\n    /**\n     * the same like query but ...\n     * ... it will also pass data through fromDb()\n     *\n     * uses internally fromDb();\n     *\n     * NOTE: Uses .query() internally\n     *\n     * uses: fromDb\n     */\n    const data2 = await man.fetch(`select * from ...`, {...params});\n\n    /**\n     * Extract data\n     * @throws Error - if found more then one\n     * @return undefined - if nothing found, object if found one\n     *\n     * NOTE: Uses .query() internally\n     *\n     * uses: fromDb\n     */\n    const row1 = await man.fetchOne(`select * from ...`, {...params});\n\n    /**\n     * Returns value from first column of first found row\n     *\n     * Extract data\n     * @throws Error - if found more then one\n     * @return undefined - if nothing found, object if found one\n     *\n     * NOTE: Uses .fetchOne() internally - inherites .fetchOne() throws\n     *\n     * uses: fromDb\n     */\n    const count1 = await man.fetchColumn(`select count(*) c from ...`, {...params});\n\n    /**\n     * Count all rows in table\n     *\n     * NOTE: Uses .fetchColumn() internally - inherites .fetchOne() throws\n     *\n     * IT'S NOT USING: fromDb\n     */\n    const count2 = await man.count();\n\n    /**\n     * Different version of fetchOne that accept only 'select' of query and id in parameters\n     *\n     * NOTE: Uses .fetchOne() internally - inherites .fetchOne() throws\n     *\n     * uses: fromDb\n     */\n    const row2 = await man.find(`id, title, ...`);\n\n    /**\n     * Returns all rows from table - quite ofthen useful if there is not many rows in table\n     *\n     * NOTE: Uses .fetch() internally\n     *\n     * uses: fromDb\n     */\n    const list1 = await man.findAll();\n\n    /**\n     * NOTE: Uses .query() internally\n     *\n     * uses: toDb\n     */\n    const newRowId = await man.insert({\n        col1: 'col1 value',\n        col2: 'col2 value',\n        ...\n    });\n\n    /**\n     * NOTE: Uses .query() internally\n     *\n     * @param object entity\n     * @param string|integer|object id\n     * @return affectedRows\n     * uses: toDb\n     */\n    const affectedRows1 = await man.update({\n        col1: 'col1 value',\n        col2: 'col2 value',\n        ...\n    }, {id: 'idvalue'});\n\n    /**\n     * NOTE: Uses .query() internally\n     *\n     * @param string|integer|object id\n     * @return affectedRows\n     */\n    const affectedRows2 = await man.delete({id: 'idvalue'});\n\n    /**\n     * Create transaction internally if there is no trx object given in first parameter\n     */\n    await this.transactify(trx, async trx =\u003e {\n\n        const id = await this.insert(trx, {\n            title,\n        });\n    });\n\n    /**\n     * This way you are using internally .transaction() method on native knex object\n     */\n    await this.transactify(async trx =\u003e {\n\n        const id = await this.insert(trx, {\n            title,\n        });\n    });\n\n    /**\n     * In order to stop passing data before and after fetching\n     * and puting to database through toDb and fromDb\n     * there is a way to tell to ignore those functions\n     */\n    await this.find(Opt({\n        fromDb  : false, // by default is true so always data are passed through fromDB if it's defined\n        toDb    : false, // by default is true so always data are passed through toDb if it's defined\n        both    : false, // disable passing through fromDb \u0026 toDb using one flag\n    }), 1)\n\n    /**\n     * Flag to print all queries to the console\n     */\n    await this.find(Opt({\n        debug: true, // def: false, might be also number it will then\n        // define how deep log.dump() should inspect objects\n    }), 1);\n\n    /**\n     * If debug flag is the only parameter that you want to pass use shortcut\n     */\n    await this.find(true, 1);\n\n\n\n    /**\n     * Example method of manager with cascading transaction\n     */\n    {\n        // ...\n        update: async function (...args) {\n\n            let [debug, trx, entity, id] = a(args);\n\n            return await this.transactify(trx, async trx =\u003e {\n\n                if (Array.isArray(entity.roles)) {\n\n                    await this.updateRoles(trx, id, entity.roles)\n                }\n\n                return prototype.prototype.update.call(this, debug, trx, entity, id);\n            });\n        },\n        // ...\n    }\n\n}());\n\n```\n\nFor more informations see [source code](src/mysql.js)\n\n# Debugging and transactions\n\nAll above methods accept additional extra parameters:\n\n- debug - boolean (default false)\n  parameter force to pring internal queries to the cli console\n- trx - function (default empty)\n  optional parameter to pass outher transaction object in order to execute logic of specific method inside external transaction\n\ntypes function and boolean are reserved for this two parameters in all above methods\n\nExample use case:\n\n- [transactions](migrations/src/migration/1545125154513-auto.ts)\n\n# Nested set\n\n```javascript\n// tags.js\nconst abstract = require(\"knex-abstract\");\n\nconst extend = abstract.extend;\n\nconst prototype = abstract.prototype_common;\n\nconst nestedset = require(\"knex-abstract/nestedset\");\n\nconst table = \"tags\";\n\nconst id = \"id\";\n\nmodule.exports = (knex) =\u003e\n  extend(\n    knex,\n    prototype,\n    Object.assign(\n      nestedset({\n        columns: {\n          l: \"l\",\n          r: \"r\",\n          level: \"level\",\n          pid: \"parent_id\",\n          sort: \"sort\",\n        },\n      }),\n      {\n        initialize: () =\u003e {}, // other custom methods\n      }\n    ),\n    table,\n    id\n  );\n```\n\nAnd from now on manager will have extra available methods:\n\n```javascript\nconst knex = require(\"../../src\");\n\n(async function () {\n  const man = knex().model.tags;\n\n  // this method will initialize columns level, sort, l, r\n  // and if root already exist this method will check if it's valid\n  const root = await man.treeInit({\n    title: \"root\",\n  });\n\n  // will find element with only id, parent_id, level, sort, l, r columns\n  // columns will be normalized to regular names pid, level, sort, l, r even if real columns in\n  // database are different\n  const node = await man.treeFindOne();\n\n  // ... and others\n})();\n```\n\n## Internal calls of methods\n\n```javascript\nconst abstract = require(\"knex-abstract\");\n\nconst { Opt } = abstract;\n\nconst extend = abstract.extend;\n\nconst prototype = abstract.prototype_common;\n\nconst table = \"tags\";\n\nconst id = \"id\";\n\nconst ns = nestedset({\n  columns: {\n    l: \"l\",\n    r: \"r\",\n    level: \"level\",\n    pid: \"parent_id\",\n    sort: \"sort\",\n  },\n});\n\nmodule.exports = (knex) =\u003e\n  extend(\n    knex,\n    prototype,\n    Object.assign({}, ns, {\n      update: async function (...args) {\n        let [opt, trx, entity, id] = a(args);\n\n        const { generatePathStop } = opt;\n\n        return await this.transactify(trx, async (trx) =\u003e {\n          const ret = await prototype.prototype.update.call(this, opt, trx, entity, id);\n\n          if (generatePathStop !== true) {\n            await this.generatePath(\n              Opt({\n                ...opt,\n                generatePathStop: true,\n              }),\n              trx,\n              id\n            );\n          }\n\n          return ret;\n        });\n      },\n\n      treeDelete: async function (...args) {\n        // standalone\n\n        let [opt, trx, id] = a(args);\n\n        return await this.transactify(trx, async (trx) =\u003e {\n          const ret = await ns.treeDelete.call(this, ...args);\n\n          await this.generatePath(opt, trx, id);\n\n          return ret;\n        });\n      },\n\n      treeMoveBefore: async function (...args) {\n        // Calls internally treeMoveToNthChild 1-2\n      },\n      treeMoveAfter: async function (...args) {\n        // Calls internally treeMoveToNthChild 1-2\n      },\n\n      treeCreateBefore: async function (...args) {\n        // Calls internally treeCreateAsNthChild 1-3\n      },\n      treeCreateAfter: async function (...args) {\n        // Calls internally treeCreateAsNthChild 1-3\n      },\n\n      treeMoveToNthChild: async function (...args) {\n        // Calls internally treeCreateAsNthChild 2-3\n      },\n      treeCreateAsNthChild: async function (...args) {\n        // standalone\n\n        let [opt, trx, opt2 = {}] = a(args);\n\n        return await this.transactify(trx, async (trx) =\u003e {\n          const ret = await ns.treeCreateAsNthChild.call(this, ...args);\n\n          await this.generatePath(opt, trx, opt2.sourceId);\n\n          return ret;\n        });\n      },\n    }),\n    table,\n    id\n  );\n```\n\n# Single file example\n\n```js\nconst path = require(\"path\");\n\nconst log = require(\"inspc\");\n\nconst knex = require(\"../../src\");\n\nconst extend = knex.extend;\n\nconst prototype = knex.prototype;\n\nconst config = require(path.resolve(__dirname, \"..\", \"ormconfig.js\"));\n\nknex.init({\n  def: \"mysql\",\n  mysql: {\n    client: \"mysql\",\n    connection: {\n      host: config.host,\n      port: config.port,\n      user: config.username,\n      password: config.password,\n      database: config.database,\n      charset: \"utf8\",\n      multipleStatements: true,\n    },\n    pool: {\n      afterCreate: function (conn, cb) {\n        conn.query(`SET SESSION sql_mode=(SELECT REPLACE(@@SESSION.sql_mode,'ONLY_FULL_GROUP_BY',''))`, function (err) {\n          cb(err, conn);\n        });\n      },\n      min: 2,\n      max: 6,\n      createTimeoutMillis: 3000,\n      acquireTimeoutMillis: 30000,\n      idleTimeoutMillis: 30000,\n      reapIntervalMillis: 1000,\n      createRetryIntervalMillis: 100,\n    },\n    acquireConnectionTimeout: 60000,\n    models: {\n      common: (knex) =\u003e extend(knex, prototype, {}),\n    },\n  },\n});\n\n(async function () {\n  try {\n    const man = knex().model.common;\n\n    const migrationsTableName = config.migrationsTableName || \"migrations\";\n\n    let count = await man.fetchColumn(\"select count(*) c from ??\", [migrationsTableName]);\n\n    process.stdout.write(String(count));\n  } catch (e) {\n    log.dump({\n      mcountdb_catch_error: e,\n    });\n  }\n\n  process.exit(0);\n})();\n```\n\n# Dev notes\n\n```bash\n\ngit clone https://github.com/stopsopa/knex-abstract.git\ncd knex-abstract\nmake up\ncp .env.dist .env\nnpm install --global nodemon\n\n# this exchanges the package.json with the package_prod\nmake yarn\n\nmake link\n\ncp migrations/ormconfig.js.mysql migrations/ormconfig.js\n# node recreate-db.js [dangerous] # this will recreate empty db in mysql only\n(cd migrations \u0026\u0026 make migrate)\n\ncp migrations/ormconfig.js.pg migrations/ormconfig.js\n# psql -c 'create database knex;' -U postgres # if needed only\nmake mrun\n\nmake manual\n\n# then if you run\nmake cc\n# you can go to\nhttp://localhost:8080/\n# to test manally\n\n```\n\nChanging the package:\n\n- before you commit the changes be sure you run `make cp` before (to bring proper package.json files)\n- commit the changes but do not push them to git!!\n- `make u` (this will push and publish the changes to npm/git)\n- if you pushed accidentially then run `make uf` (force mode)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstopsopa%2Fknex-abstract","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstopsopa%2Fknex-abstract","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstopsopa%2Fknex-abstract/lists"}