{"id":23773532,"url":"https://github.com/beenotung/better-sqlite3-proxy","last_synced_at":"2025-08-04T09:32:43.879Z","repository":{"id":39816185,"uuid":"465390771","full_name":"beenotung/better-sqlite3-proxy","owner":"beenotung","description":"Efficiently proxy sqlite tables and access data as typical array of objects","archived":false,"fork":false,"pushed_at":"2025-07-08T04:53:41.000Z","size":198,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-07-08T06:43:55.434Z","etag":null,"topics":["better-sqlite3","data-access-library","data-access-object","proxy","sqlite","sync","typescript-library"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/better-sqlite3-proxy","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/beenotung.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,"zenodo":null}},"created_at":"2022-03-02T16:48:43.000Z","updated_at":"2025-07-08T04:53:45.000Z","dependencies_parsed_at":"2024-04-09T20:43:47.220Z","dependency_job_id":"c9eab9dc-ebbd-4c7c-8b9e-bcefc15880b9","html_url":"https://github.com/beenotung/better-sqlite3-proxy","commit_stats":{"total_commits":87,"total_committers":1,"mean_commits":87.0,"dds":0.0,"last_synced_commit":"8e4d150491acb0077aa3e80bd68b067cde8048c8"},"previous_names":[],"tags_count":43,"template":false,"template_full_name":null,"purl":"pkg:github/beenotung/better-sqlite3-proxy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beenotung%2Fbetter-sqlite3-proxy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beenotung%2Fbetter-sqlite3-proxy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beenotung%2Fbetter-sqlite3-proxy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beenotung%2Fbetter-sqlite3-proxy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/beenotung","download_url":"https://codeload.github.com/beenotung/better-sqlite3-proxy/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beenotung%2Fbetter-sqlite3-proxy/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268675515,"owners_count":24288285,"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","status":"online","status_checked_at":"2025-08-04T02:00:09.867Z","response_time":79,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["better-sqlite3","data-access-library","data-access-object","proxy","sqlite","sync","typescript-library"],"created_at":"2025-01-01T05:41:05.253Z","updated_at":"2025-08-04T09:32:43.858Z","avatar_url":"https://github.com/beenotung.png","language":"TypeScript","readme":"# better-sqlite3-proxy\n\nEfficiently proxy sqlite tables and access data as typical array of objects.\nPowered by [better-sqlite3](https://github.com/WiseLibs/better-sqlite3)🔋\n\n[![npm Package Version](https://img.shields.io/npm/v/better-sqlite3-proxy)](https://www.npmjs.com/package/better-sqlite3-proxy)\n\n## Features\n\n- [x] Type safety support for each table\n- [x] support common types of table\n  - [x] with schema (e.g. id, user_id, title, content)\n  - [x] with json (e.g. id, value)\n- [x] auto run sqlite statements, supports:\n  - [x] create table (only for key-value proxy)\n  - [x] select\n  - [x] insert\n  - [x] update\n  - [x] delete\n- [x] auto resolve reference row from foreign key into nested objects like [ref-db](https://github.com/beenotung/ref-db)\n- [x] auto convert column values into sqlite3 format\n  - [x] convert `true`/`false` to `1`/`0`\n  - [x] convert `Date` instance to GMT timestamp\n  - [x] support searching `null` / `not null` columns\n- [x] extra helper functions:\n  - [x] toSqliteTimestamp (date): string\n  - [x] fromSqliteTimestamp (string_or_date): Date\n  - [x] getTimes (row, fields?): Record\u003cField, Date | null\u003e\n  - [x] seedRow (table, filter, extra?): number\n  - [x] upsert (table, key, data): number\n  - [x] getId (table, key, value): number | null\n\n### Array Operations Mapping\n\n| Array Operation                 | Mapped SQL Operation                  |\n| ------------------------------- | ------------------------------------- |\n| `array.push(...object)`         | insert                                |\n| `array[id] = object`            | insert or update                      |\n| `update(array, id, partial)`    | update                                |\n| `find(array, filter)`           | select where filter limit 1           |\n| `filter(array, filter)`         | select where filter                   |\n| `pick(array, columns, filter?)` | select columns where filter           |\n| `count(array, filter)`          | select count where filter             |\n| `delete array[id]`              | delete where id                       |\n| `del(array, filter)`            | delete where filter                   |\n| `array.length = length`         | delete where id \u003e length              |\n| `array.slice(start, end)`       | select where id \u003e= start and id \u003c end |\n\nfor-of loop, `array.forEach(fn)`, `array.filter(fn)` and `array.map(fn)` are also supported, they will receive proxy-ed rows.\n\nTips: You can use for-of loop instead of `array.forEach(fn)` if you may terminate the loop early\n\nTips: You can use `filter(partial)` instead of `array.filter(fn)` for better performance\n\nTips: You can use `pick(array, columns, filter?)` instead of `array.map(fn)` for better performance\n\nTips: You can use `update(array, id, partial)` instead of `Object.assign(row, partial)` to update multiple columns in batch\n\nPro Tips: If you need complex query that can be expressed in sql, use prepared statement will have fastest runtime performance.\n\n### Lazy Evaluation\n\nThe results from mapped operations are proxy-ed object identified by id.\nGetting the properties on the object will trigger select on corresponding column, and\nsetting the properties will trigger update on corresponding column.\n\n## Usage Example\n\nRemark: `@beenotung/better-sqlite3-helper` is a fork of `better-sqlite3-helper`. It updates the dependency on better-sqlite3 to v8+ which includes arm64 prebuilds for macOS.\n\n\u003cdetails\u003e\n\u003csummary\u003eProxy Relational Tables (click to expand)\n\nMore Examples in [schema-proxy.spec.ts](./test/schema-proxy.spec.ts)\n\n\u003c/summary\u003e\n\n```typescript\nimport DB from '@beenotung/better-sqlite3-helper'\nimport { proxySchema, unProxy, find, filter } from 'better-sqlite3-proxy'\n\nlet db = DB({\n  path: 'dev.sqlite3',\n  migrate: {\n    migrations: [\n      /* sql */ `\n-- Up\ncreate table if not exists user (\n  id integer primary key\n, username text not null unique\n);\n-- Down\ndrop table user;\n`,\n      /* sql */ `\n-- Up\ncreate table if not exists post (\n  id integer primary key\n, user_id integer not null references user (id)\n, content text not null\n, created_at timestamp not null default current_timestamp\n);\n-- Down\ndrop table post;\n`,\n    ],\n  },\n})\n\ntype DBProxy = {\n  user: User[]\n  post: Post[]\n}\ntype User = {\n  id?: number\n  username: string\n}\ntype Post = {\n  id?: number\n  user_id: number\n  content: string\n  created_at?: string\n  author?: User\n}\n\nlet proxy = proxySchema\u003cDBProxy\u003e(db, {\n  user: ['id', 'username'], // specify columns explicitly or leave it empty to auto-scan from create-table schema\n  post: [\n    ['author', { field: 'user_id', table: 'user' }], // link up reference fields\n  ],\n})\n\n// insert record\nproxy.user[1] = { username: 'alice' }\nproxy.user.push({ username: 'Bob' })\nproxy.post.push({ user_id: 1, content: 'Hello World' })\n\n// select a specific column\nconsole.log(proxy.user[1].username) // 'alice'\n\n// select a specific column from reference table\nconsole.log(proxy.post[1].author?.username) // 'alice'\n\n// select all columns of a record\nconsole.log(unProxy(proxy.post[1])) // { id: 1, user_id: 1, content: 'Hello World', created_at: '2022-04-21 23:30:00'}\n\n// update a specific column\nproxy.user[1].username = 'Alice'\n\n// update multiple columns\nproxy.post[1] = {\n  content: 'Hello SQLite',\n  created_at: '2022-04-22 08:30:00',\n} as Partial\u003cPost\u003e as Post\n\n// find by columns\nconsole.log(find(proxy.user, { username: 'Alice' })?.id) // 1\n\n// filter by columns\nconsole.log(filter(proxy.post, { user_id: 1 })[0].content) // 'Hello SQLite\n\n// delete record\ndelete proxy.user[2]\nconsole.log(proxy.user.length) // 1\n\n// truncate table\nproxy.post.length = 0\nconsole.log(proxy.post.length) // 0\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eProxy Key-Value Records (click to expand)\n\nMore Examples in [key-value.spec.ts](./test/key-value-proxy.spec.ts)\n\n\u003c/summary\u003e\n\n```typescript\nimport DB from '@beenotung/better-sqlite3-helper'\nimport { proxyKeyValue, find, filter } from 'better-sqlite3-proxy'\n\nexport let db = DB({\n  path: 'dev.sqlite3',\n  migrate: false,\n})\n\ntype DBProxy = {\n  users: {\n    id: number\n    username: string\n  }[]\n}\n\nlet proxy = proxyKeyValue\u003cDBProxy\u003e(db)\n\n// auto create users table, then insert record\nproxy.users[1] = { id: 1, username: 'alice' }\nproxy.users.push({ id: 2, username: 'Bob' })\n\n// select from users table\nconsole.log(proxy.users[1]) // { id: 1, username: 'alice' }\n\n// update users table\nproxy.users[1] = { id: 1, username: 'Alice' }\nconsole.log(proxy.users[1]) // { id:1, username: 'Alice' }\n\n// find by columns\nconsole.log(find(proxy.users, { username: 'Alice' })?.id) // 1\n\n// filter by columns\nconsole.log(filter(proxy.users, { username: 'Bob' })[0].id) // 2\n\n// delete record\ndelete proxy.users[2]\nconsole.log(proxy.users.length) // 1\n\n// truncate table\nproxy.users.length = 0\nconsole.log(proxy.users.length) // 0\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eHelper Functions Examples (click to expand)\n\nMore Examples in [helpers.spec.ts](./test/helpers.spec.ts)\n\n\u003c/summary\u003e\n\n```typescript\nimport DB from '@beenotung/better-sqlite3-helper'\nimport {\n  proxySchema,\n  toSqliteTimestamp,\n  fromSqliteTimestamp,\n  getTimes,\n  seedRow,\n  getId,\n} from 'better-sqlite3-proxy'\nimport { proxy } from './proxy'\n\n// Timestamp helpers\nlet timestamp = toSqliteTimestamp(new Date())\nconsole.log(timestamp) // '2024-01-15 10:30:00'\n\nlet date = fromSqliteTimestamp(timestamp)\nconsole.log(date) // Date object\n\n// Select timestamps and convert to Date objects (from ISO string in GMT timezone)\nlet article = proxy.article[1]\nlet times = getTimes(article, ['created_at', 'updated_at'])\nconsole.log(times.created_at) // Date object\n\n// Update existing row or insert new row\nlet region_id = seedRow(proxy.region, { code: 'HK' }, { name: 'Hong Kong' })\nconsole.log(region_id) // 1\n\n// Simplified version of seedRow when the table only has one unique key\nlet tag_id = getId(proxy.hashtag, 'tag', 'linux')\nconsole.log(tag_id) // 1\n```\n\n\u003c/details\u003e\n\n## License\n\nThis project is licensed with [BSD-2-Clause](./LICENSE)\n\nThis is free, libre, and open-source software. It comes down to four essential freedoms [[ref]](https://seirdy.one/2021/01/27/whatsapp-and-the-domestication-of-users.html#fnref:2):\n\n- The freedom to run the program as you wish, for any purpose\n- The freedom to study how the program works, and change it so it does your computing as you wish\n- The freedom to redistribute copies so you can help others\n- The freedom to distribute copies of your modified versions to others\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbeenotung%2Fbetter-sqlite3-proxy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbeenotung%2Fbetter-sqlite3-proxy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbeenotung%2Fbetter-sqlite3-proxy/lists"}