{"id":13440822,"url":"https://github.com/oguimbal/pg-mem","last_synced_at":"2025-05-12T15:29:25.492Z","repository":{"id":36950116,"uuid":"280279657","full_name":"oguimbal/pg-mem","owner":"oguimbal","description":"An in memory postgres DB instance for your unit tests","archived":false,"fork":false,"pushed_at":"2025-02-20T09:53:58.000Z","size":2332,"stargazers_count":2136,"open_issues_count":177,"forks_count":103,"subscribers_count":16,"default_branch":"master","last_synced_at":"2025-04-23T17:17:36.936Z","etag":null,"topics":["deno","hacktoberfest","mocha","node","node-postgres","nodejs","pg-mem","pg-promise","postgresql","slonik","sql-syntax-parser","typeorm","typescript","unit-testing","unit-tests"],"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/oguimbal.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","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},"funding":{"patreon":"oguimbal","ko_fi":"oguimbal"}},"created_at":"2020-07-16T23:27:08.000Z","updated_at":"2025-04-23T16:46:22.000Z","dependencies_parsed_at":"2024-01-11T12:04:18.800Z","dependency_job_id":"eadfbc26-c900-43bb-8b64-2414134646af","html_url":"https://github.com/oguimbal/pg-mem","commit_stats":{"total_commits":580,"total_committers":40,"mean_commits":14.5,"dds":0.1586206896551724,"last_synced_commit":"8f84e85fb77dc1a52c2cdc93b7a218078a0907f8"},"previous_names":[],"tags_count":114,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oguimbal%2Fpg-mem","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oguimbal%2Fpg-mem/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oguimbal%2Fpg-mem/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oguimbal%2Fpg-mem/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/oguimbal","download_url":"https://codeload.github.com/oguimbal/pg-mem/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253765481,"owners_count":21960737,"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":["deno","hacktoberfest","mocha","node","node-postgres","nodejs","pg-mem","pg-promise","postgresql","slonik","sql-syntax-parser","typeorm","typescript","unit-testing","unit-tests"],"created_at":"2024-07-31T03:01:26.671Z","updated_at":"2025-05-12T15:29:25.465Z","avatar_url":"https://github.com/oguimbal.png","language":"TypeScript","funding_links":["https://patreon.com/oguimbal","https://ko-fi.com/oguimbal"],"categories":["Packages","HarmonyOS","TypeScript","Repository","typescript","包"],"sub_categories":["Database","Windows Manager","数据库"],"readme":"\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://npmjs.org/package/pg-mem\"\u003e\u003cimg src=\"http://img.shields.io/npm/v/pg-mem.svg\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://npmjs.org/package/pg-mem\"\u003e\u003cimg src=\"https://img.shields.io/npm/dm/pg-mem.svg\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://david-dm.org/oguimbal/pg-mem\"\u003e\u003cimg src=\"https://david-dm.org/oguimbal/pg-mem.svg\"\u003e\u003c/a\u003e\n  \u003cimg src=\"https://github.com/oguimbal/pg-mem/workflows/CI/badge.svg\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"./.github/pg_mem.png\" width=\"200\"\u003e\n\u003c/p\u003e\n\n \u003ch3 align=\"center\"\u003epg-mem is an experimental in-memory emulation of a postgres database.\u003c/h3\u003e\n\n\u003cp align=\"center\"\u003e\n❤ It works both in Node or in the browser.\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n⭐ this repo if you like this package, it helps to motivate me :)\n\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  👉 See it in action with \u003ca href=\"https://oguimbal.github.io/pg-mem-playground/\"\u003epg-mem playground\u003c/a\u003e\n\u003c/p\u003e\n\n- [Usage](#-usage)\n- [Features](#-features)\n- [Libraries adapters](#-libraries-adapters)\n- [Inspection](#-inspection)\n- [Development](#-development)\n- [FAQ](https://github.com/oguimbal/pg-mem/wiki/FAQ)\n\n# 📐 Usage\n\n## Using Node.js\n\nAs always, it starts with an:\n\n```bash\nnpm i pg-mem --save\n```\n\nThen, assuming you're using something like webpack, if you're targeting a browser:\n\n```typescript\nimport { newDb } from \"pg-mem\";\n\nconst db = newDb();\ndb.public.many(/* put some sql here */);\n```\n\n## Using Deno\n\nPretty straightforward :)\n\n```typescript\nimport { newDb } from \"https://deno.land/x/pg_mem/mod.ts\";\n\nconst db = newDb();\ndb.public.many(/* put some sql here */);\n```\n\n## Only use the SQL syntax parser\n\n❤ Head to the [pgsql-ast-parser](https://github.com/oguimbal/pgsql-ast-parser) repo\n\n## ⚠ Disclaimer\n\nThe sql syntax parser is [home-made](https://github.com/oguimbal/pgsql-ast-parser). Which means that some features are not implemented, and will be considered as invalid syntaxes.\n\nThis lib is quite new, so forgive it if some obvious pg syntax is not supported !\n\n... And open an issue if you feel like a feature should be implemented :)\n\nMoreover, even if I wrote hundreds of tests, keep in mind that this implementation is a best effort to replicate PG.\nKeep an eye on your query results if you perform complex queries.\nPlease file issues if some results seem incoherent with what should be returned.\n\nFinally, I invite you to read the below section to have an idea of you can or cannot do.\n\n# 🔍 Features\n\n## Rollback to a previous state\n\n`pg-mem` uses immutable data structures ([here](https://www.npmjs.com/package/immutable) and [here](https://www.npmjs.com/package/functional-red-black-tree)),\nwhich means that you can have restore points for free!\n\nThis is super useful if you intend to use `pg-mem` to mock your database for unit tests.\n\nYou could:\n\n1. Create your schema only once (which could be a heavy operation for a single unit test)\n2. Insert test data which will be shared by all test\n3. Create a restore point\n4. Run your tests with the same db instance, executing a `backup.restore()` before each test (which instantly resets db to the state it has after creating the restore point)\n\nUsage:\n\n```typescript\nconst db = newDb();\ndb.public.none(`create table test(id text);\n                insert into test values ('value');`);\n// create a restore point \u0026 mess with data\nconst backup = db.backup();\ndb.public.none(`update test set id='new value';`);\n// restore it !\nbackup.restore();\ndb.public.many(`select * from test`); // =\u003e {test: 'value'}\n```\n\n## Custom functions\n\nYou can declare custom functions like this:\n\n```typescript\ndb.public.registerFunction({\n  name: \"say_hello\",\n  args: [DataType.text],\n  returns: DataType.text,\n  implementation: (x) =\u003e \"hello \" + x,\n});\n```\n\nAnd then use them like in SQL `select say_hello('world')`.\n\nCustom functions support overloading and variadic arguments.\n\n⚠ However, the value you return is not type checked. It MUST correspond to the datatype you provided as 'returns' (it won't fail if not, but could lead to weird bugs).\n\n## Custom types\n\nNot all pg types are implemented in pg-mem.\nThat said, most of the types are often equivalent to other types, with a format validation. pg-mem provides a way to register such types.\n\nFor instance, lets say you'd like to register the MACADDR type, which is basically a string, with a format constraint.\n\nYou can register it like this:\n\n```typescript\ndb.public.registerEquivalentType({\n  name: \"macaddr\",\n  // which type is it equivalent to (will be able to cast it from it)\n  equivalentTo: DataType.text,\n  isValid(val: string) {\n    // check that it will be this format\n    return isValidMacAddress(val);\n  },\n});\n```\n\nDoing so, you'll be able to do things such as:\n\n```sql\nSELECT '08:00:2b:01:02:03:04:05'::macaddr; -- WORKS\nSELECT 'invalid'::macaddr; -- will throw a conversion error\n```\n\nIf you feel your implementation of a type matches the standard, and would like to include it in pg-mem for others to enjoy it, please consider filing a pull request ! (tip: see the [INET](https://github.com/oguimbal/pg-mem/blob/master/src/datatypes/t-inet.ts) type implementation as an example, and the [pg_catalog index](https://github.com/oguimbal/pg-mem/blob/master/src/schema/pg-catalog/index.ts) where supported types are registered)\n\n## Extensions\n\nNo native extension is implemented (pull requests are welcome), but you can define kind-of extensions like this:\n\n```typescript\ndb.registerExtension(\"my-ext\", (schema) =\u003e {\n  // install your ext in 'schema'\n  // ex:  schema.registerFunction(...)\n});\n```\n\nStatements like `create extension \"my-ext\"` will then be supported.\n\n# 📃 Libraries adapters\n\npg-mem provides handy shortcuts to create instances of popular libraries that will be bound to pg-mem instead of a real postgres db.\n\n- pg-native\n- node-postgres (pg)\n- pg-promise (pgp)\n- slonik\n- typeorm\n- knex\n- kysely\n- mikro-orm\n\n[See the wiki for more details](https://github.com/oguimbal/pg-mem/wiki/Libraries-adapters)\n\n# 💥 Inspection\n\n## Intercept queries\n\nIf you would like to hook your database, and return ad-hoc results, you can do so like this:\n\n```typescript\nconst db = newDb();\n\ndb.public.interceptQueries((sql) =\u003e {\n  if (sql === \"select * from whatever\") {\n    // intercept this statement, and return something custom:\n    return [{ something: 42 }];\n  }\n  // proceed to actual SQL execution for other requests.\n  return null;\n});\n```\n\n## Inspect a table\n\nYou can manually inspect a table content using the `find()` method:\n\n```typescript\nfor (const item of db.public.getTable\u003cTItem\u003e(\"mytable\").find(itemTemplate)) {\n  console.log(item);\n}\n```\n\n## Manually insert items\n\nIf you'd like to insert items manually into a table, you can do this like that:\n\n```typescript\ndb.public.getTable\u003cTItem\u003e('mytable').insert({ /* item to insert */ }))\n```\n\n## Subscribe to events\n\nYou can subscribe to some events, like:\n\n```typescript\nconst db = newDb();\n\n// called on each successful sql request\ndb.on(\"query\", (sql) =\u003e {});\n// called on each failed sql request\ndb.on(\"query-failed\", (sql) =\u003e {});\n// called on schema changes\ndb.on(\"schema-change\", () =\u003e {});\n// called when a CREATE EXTENSION schema is encountered.\ndb.on(\"create-extension\", (ext) =\u003e {});\n```\n\n## Experimental events\n\n`pg-mem` implements a basic support for indices.\n\nThese handlers are called when a request cannot be optimized using one of the created indices.\n\nHowever, a real postgres instance will be much smarter to optimize its requests... so when `pg-mem` says \"this request does not use an index\", dont take my word for it.\n\n```typescript\n// called when a table is iterated entirely (ex: 'select * from data where notIndex=3' triggers it)\ndb.on('seq-scan', () =\u003e {});\n\n// same, but on a specific table\ndb.getTable('myTable').on('seq-scan', () = {});\n\n// will be called if pg-mem did not find any way to optimize a join\n// (which leads to a O(n*m) lookup with the current implementation)\ndb.on('catastrophic-join-optimization', () =\u003e {});\n```\n\n# 🙋‍♂️ FAQ\n\n- [Why this instead of Docker ?](https://github.com/oguimbal/pg-mem/wiki/FAQ#-why-use-pg-mem-instead-of-an-instance-of-postgres-in-docker-) _TLDR : It's faster. Docker is overkill._\n- [What if I need an extension like uuid-ossp ?](https://github.com/oguimbal/pg-mem/wiki/FAQ#-what-if-i-need-an-extension-like-uuid-ossp-) _TLDR: You can mock those_\n- [How to import my production schema in pg-mem ?](https://github.com/oguimbal/pg-mem/wiki/FAQ#-how-to-import-my-production-schema-in-pg-mem-) _TLDR: pg_dump with the right args_\n- [Does pg-mem supports sql migrations ?](https://github.com/oguimbal/pg-mem/wiki/FAQ#-does-pg-mem-support-sql-migrations-scripts-) _TLDR: yes._\n- [Does pg-mem supports plpgsql/other scripts/\"create functions\"/\"do statements\" ?](https://github.com/oguimbal/pg-mem/wiki/FAQ#-how-to-use-plpgsql-or-other-scripts-) _TLDR: kind of..._\n\nDetailed answers [in the wiki](https://github.com/oguimbal/pg-mem/wiki/FAQ)\n\n# ⚠️ Current limitations\n\n- Materialized views are implemented as views (meaning that they are always up-to-date, without needing them to refresh)\n- Indices implementations are basic\n- No support for timezones\n- All number-like types are all handled as javascript numbers, meaning that types like `numeric(x,y)` could not behave as expected.\n\n# 🐜 Development\n\nPull requests are welcome :)\n\nUnit tests are ran using [Bun](https://bun.sh/), which you will have to install to run tests.\n\n## Run all tests\n\n```bash\nbun test\n```\n\n## Debug a test\n\nUsing vscode:\n\n1. Add a `.only` on the test you'd like to debug\n2. Just hit F5 (or execute via the debugger tab), which should launch your test with debugger attached\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foguimbal%2Fpg-mem","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foguimbal%2Fpg-mem","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foguimbal%2Fpg-mem/lists"}