{"id":15295092,"url":"https://github.com/fingerprintjs/nice-pg-sql-toolkit","last_synced_at":"2025-04-13T15:57:27.649Z","repository":{"id":42779620,"uuid":"274513407","full_name":"fingerprintjs/nice-pg-sql-toolkit","owner":"fingerprintjs","description":"Nice PG SQL toolkit. Loves SQL. Not an ORM. Can do migrations.","archived":false,"fork":false,"pushed_at":"2023-03-05T02:41:34.000Z","size":324,"stargazers_count":10,"open_issues_count":3,"forks_count":1,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-04-10T19:59:28.500Z","etag":null,"topics":["db-migration","javascript","migrations","nodejs","postgresql","tiny","toolkit"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/fingerprintjs.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":"2020-06-23T21:34:46.000Z","updated_at":"2023-02-02T22:55:29.000Z","dependencies_parsed_at":"2024-10-14T23:20:47.258Z","dependency_job_id":"5f396711-fb74-48ac-9653-a6252b0d7074","html_url":"https://github.com/fingerprintjs/nice-pg-sql-toolkit","commit_stats":{"total_commits":39,"total_committers":3,"mean_commits":13.0,"dds":"0.28205128205128205","last_synced_commit":"ef53fb03ef54939ae3a9ca8832f5a828d0c419c2"},"previous_names":["fingerprintjs/weerm"],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fingerprintjs%2Fnice-pg-sql-toolkit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fingerprintjs%2Fnice-pg-sql-toolkit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fingerprintjs%2Fnice-pg-sql-toolkit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fingerprintjs%2Fnice-pg-sql-toolkit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fingerprintjs","download_url":"https://codeload.github.com/fingerprintjs/nice-pg-sql-toolkit/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248741160,"owners_count":21154252,"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":["db-migration","javascript","migrations","nodejs","postgresql","tiny","toolkit"],"created_at":"2024-09-30T17:08:35.165Z","updated_at":"2025-04-13T15:57:27.628Z","avatar_url":"https://github.com/fingerprintjs.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ccenter\u003e\n\n![logo](https://fpjs-public.s3.amazonaws.com/oss/nice-pg-sql-toolkit/logo.jpg)\n\nNice PG SQL toolkit\n============================\n\n![build](https://github.com/fingerprintjs/nice-pg-sql-toolkit/workflows/build/badge.svg)\n\u003c/center\u003e\n\n\n🧰 Nice SQL toolkit for PG + Node (tiny, \u003c200 LOC)\n\n```\nnpm i nice-pg-sql-toolkit\nor\nyarn add nice-pg-sql-toolkit\n```\n\n## Usage\n\nYour database URL should be in `DATABASE_URL` env var, e.g.\n\n```shell script\nexport DATABASE_URL=postgres://user:password@host/database:5432\n```\n\nAlternatively you can specify a database URL as a parameter to `recreatePool`:\n\n```js\nconst db = require('nice-pg-sql-toolkit')\ndb.recreatePool({connectionString: 'postgres://localhost'})\n```\n\n### Simple usage\n_this approach is a good starting point, it uses DB-level attributes directly w/out column mapping_\n\n```js\nconst db = require('nice-pg-sql-toolkit')\n\n// find one user by email\nlet row = await db.findOne('users', {email: 'john@example.com'})\n\n\n// find all users by role\nlet rows = await db.find('users', {role: 'admin'})\n\n// find all users by multiple roles\nlet rows = await db.find('users', {role: ['admin', 'root', 'superuser']})\n\n// insert a user\nlet attrs = await db.insert('users', {email: 'john@example.com', role: 'admin'})\n// attrs will have the id attribute if you have an id primary key\n\n// update all users by role, set their access_level to 'full'\nawait db.update('users', {'access_level': 'full'}, {'role': 'admin'})\n\n// delete a user by ID\nawait db.del('users', {id: 23234554})\n\n// use inline SQL directly\nconst sql = `SELECT * FROM users WHERE firstName = $1 ORDER BY ID DESC LIMIT $2`\n// pass dollar params as a second argument as an array\nlet firstName = 'John'\nlet limit = 10\nlet rows = await db.query(sql, [firstName, limit])\n```\n\n### Define your model, for example models/user\n_this is convenient if you want to keep your logic centralized and also perform column mapping_\n\n```js\n// models/user.js\nconst db = require('nice-pg-sql-toolkit')\n\nconst TableName = 'users'\n\n// model attribute to column mapping object\nconst Columns = {\n  id: 'user_id',\n  firstName: 'first_name',\n  lastName: 'last_name',\n  createdAt: 'created_at'\n}\n\nconst findOne = async (condition) =\u003e {\n  let conditionValues = db.mapToColumns(condition, columns)\n  let row = await db.findOne(TableName, conditionValues)\n  return db.mapFromColumns(row, columns)\n}\n\nconst find = async (condition) =\u003e {\n  let conditionValues = db.mapToColumns(condition, columns)\n  let rows = await db.find(TableName, conditionValues)\n  return rows.map((row) =\u003e db.mapFromColumns(row, columns))\n}\n\nconst create = async (attrs) =\u003e {\n  let columnValues = db.mapToColumns(attrs, columns)\n  return await db.insert(TableName, columnValues)\n}\n\nconst update = async (condition, attrs) =\u003e {\n  let conditionValues = db.mapToColumns(condition, columns)\n  let columnValues = db.mapToColumns(attrs, columns)\n  return await db.update(TableName, columnValues, conditionValues)\n}\n\nconst del = async (condition) =\u003e {\n  let conditionValues = db.mapToColumns(condition, columns)\n  return await db.del(TableName, conditionValues)\n}\n```\n\n```js\n// Now you can use your model everywhere\nconst user = require('/models/user')\n\n// find one (e.g. by ID)\nlet user = await User.findOne({id: 3956})\n// if no user is found, null will be returned\n\n\n// find multiple users\nlet users = await User.find({lastName: 'Smith'})\n// if no users were found, empty array will be returned\n\n\n// add a new user\nlet user = await User.create({firstName: 'John', lastName: 'Smith'})\n\n\n// update existing user\n// update a user by ID\nawait User.update({id: 3956}, {lastName: 'Bunyan'})\n\n\n// delete user\n// delete a user by ID\nawait User.del({id: 3956})\n```\n\n### Using transactions\n\n```js\n// using transaction requires wrapping everything in a transaction and\n// passing the current transaction as a last argument\nlet userAudit = await db.withTransaction(async (tr) =\u003e {\n  await db.update('users', {id: 9363}, {lastName: 'Bunyan'}, tr)\n  return await db.create('users_audit', {entity: 'User', op: 'update', args: [{lastName: 'Bunyan'}]}, tr)\n})\n// note that the return value from the callback will be returned by withTransaction function\n```\n\nIf you want to execute certain actions after the transaction is rolled back,\nuse the second function argument for this.\n\n```js\nlet onRollback = () =\u003e {\n  // cleanup external resources\n  // e.g. // payment gateway rollback etc\n}\nlet res = await db.withTransaction(tr =\u003e {/* do something in transaction.. */}, onRollback)\n```\n\n### Unique index violation\n\n```js\n  // checking error type will tell you if it's a unique index violation\n  try {\n    let user = await db.create('users', {email: 'smith@example.com'})\n  } catch(e) {\n    if(e instanceof db.UniqueIndexError) {\n      console.log('Unique index violation on table: users, columns:', e.columns)\n    }\n  }\n```\n\n### Using migrations\n\nThis toolkit comes with a simple migration runner.\n\nTo use it, create a directory with SQL scripts inside.\n\nEvery DB version change requires two scripts: up and down.\n\n```sh\ndb/migrations\n├── 0001_create_table1.up.sql\n└── 0001_drop_table1.down.sql\n    └──version └──name └── up or down\n```\n\nExample of `up` script:\n\n```sql\ncreate table users (\n  id serial primary key,\n  email text unique\n);\n```\n\nExample of `down` script:\n\n```sql\ndrop table users;\n```\nYou can place multiple create/drop statement in each file, they will be run inside a transaction\nand either all succeed or all fail.\n\nOnce you have the migration files ready, you have two options: run migrations with API or with CLI\n\n\n#### API\n\n```js\nimport db from 'nice-pg-sql-toolkit'\n\n// pass the migrations directory path\nconst migrator = db.createMigrator('/opt/projects/your-project/db/migrations')\n\n// to run `up`\nawait migrator.up()\n\n// to run `down`\nawait migrator.down()\n```\n\n#### CLI\n\n```shell script\n# you can use relative paths here\nyarn nice-pg-migrate db/migrations up\n\n# or\nyarn nice-pg-migrate db/migrations down\n```\n\nMigration runner will maintain a special table called `db_versions` internally\nthat will keep all applied migrations.\n\n\nMIT Licensed.\n\nCopyright FingerprintJS Inc., 2020-2021.\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffingerprintjs%2Fnice-pg-sql-toolkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffingerprintjs%2Fnice-pg-sql-toolkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffingerprintjs%2Fnice-pg-sql-toolkit/lists"}