{"id":15111562,"url":"https://github.com/gms1/node-sqlite3-orm","last_synced_at":"2025-09-27T13:30:33.595Z","repository":{"id":39363797,"uuid":"67031482","full_name":"gms1/node-sqlite3-orm","owner":"gms1","description":"ORM for SQLite and TypeScript/JavaScript","archived":true,"fork":false,"pushed_at":"2023-11-19T15:56:20.000Z","size":2408,"stargazers_count":38,"open_issues_count":2,"forks_count":5,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-09-26T00:21:35.616Z","etag":null,"topics":["connection-pool","dao","javascript","orm","sqlite3","typescript"],"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/gms1.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2016-08-31T11:30:21.000Z","updated_at":"2024-03-10T08:25:09.000Z","dependencies_parsed_at":"2023-11-19T16:48:27.290Z","dependency_job_id":null,"html_url":"https://github.com/gms1/node-sqlite3-orm","commit_stats":{"total_commits":421,"total_committers":9,"mean_commits":46.77777777777778,"dds":0.3254156769596199,"last_synced_commit":"2c08c7e2146b798ca6e8f751bd681bd552c8e631"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gms1%2Fnode-sqlite3-orm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gms1%2Fnode-sqlite3-orm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gms1%2Fnode-sqlite3-orm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gms1%2Fnode-sqlite3-orm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gms1","download_url":"https://codeload.github.com/gms1/node-sqlite3-orm/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234438039,"owners_count":18832611,"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":["connection-pool","dao","javascript","orm","sqlite3","typescript"],"created_at":"2024-09-26T00:21:18.173Z","updated_at":"2025-09-27T13:30:33.254Z","avatar_url":"https://github.com/gms1.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# node-sqlite3-orm\n\n**THIS PROJECT HAS BEEN MOVED!**\n\n**THE NEW LOCATION IS HERE: [sqlite3orm](https://github.com/gms1/HomeOfThings/tree/master/packages/node/sqlite3orm)**\n\n[![Version](https://img.shields.io/npm/v/sqlite3orm)](https://www.npmjs.com/package/sqlite3orm)\n[![Build Status](https://github.com/gms1/node-sqlite3-orm/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/gms1/node-sqlite3-orm/actions/workflows/build.yml)\n[![Coverage Status](https://codecov.io/gh/gms1/node-sqlite3-orm/branch/master/graph/badge.svg)](https://codecov.io/gh/gms1/node-sqlite3-orm)\n[![DeepScan Grade](https://deepscan.io/api/projects/699/branches/1107/badge/grade.svg)](https://deepscan.io/dashboard/#view=project\u0026pid=699\u0026bid=1107)\n\n[![License](https://img.shields.io/npm/l/sqlite3orm)](https://github.com/gms1/node-sqlite3-orm/blob/master/LICENSE)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](http://makeapullrequest.com)\n\nThis module allows you to map your model, written in JavaScript or TypeScript, to a database schema using SQLite Version 3.\n**node-sqlite3-orm** is designed to work with new JavaScript _Decorators_, _Promises_ and the _async/await_ feature.\nIt offers connection pool, automatic upgrades and online backups as well as typesafe database queries and refactoring, using a filter syntax designed to serialize safely without any SQL injection possibility\n\n\u003e NOTE: Your contribution is highly welcome! Feel free to pick-up a TODO-item or add yours.\n\nIf you are using **NestJs** please see [@HomeOfThings/nest-sqlite3](https://www.npmjs.com/package/@homeofthings/nestjs-sqlite3)\n\n## Mapping Introduction\n\n**node-sqlite3-orm** provides you with the ability to create the database schema for the mapped model and to store and retrieve the mapped data to and from the database,\n\n```TypeScript\nimport {table, id, field, index, fk, FieldOpts, TableOpts} from 'sqlite3orm';\n\n@table({name: 'USERS'})\nclass User {\n  @id({name: 'user_id', dbtype: 'INTEGER NOT NULL'})\n  userId!: number;\n\n  @field({name: 'user_loginname', dbtype: 'TEXT NOT NULL'})\n  userLoginName!: string;\n\n  @field({name: 'user_json', dbtype: 'TEXT', isJson: true})\n  userJsonData: any;\n\n  @field({name: 'user_deleted'})\n  deleted?: boolean;\n\n}\n\n@table({name: 'CONTACTS', autoIncrement: true})\nclass Contact {\n  @id({name: 'contact_id', dbtype: 'INTEGER NOT NULL'})\n  contactId!: number;\n\n  @field({name: 'contact_email', dbtype: 'TEXT'})\n  emailAddress?: string;\n\n  @field({name: 'contact_mobile', dbtype: 'TEXT'})\n  mobile?: string;\n\n  @field({name: 'user_id', dbtype: 'INTEGER NOT NULL'})\n  @fk('fk_user_contacts', 'USERS', 'user_id')\n  @index('idx_contacts_user')\n  userId!: number;\n}\n```\n\nWith **node-sqlite3-orm** you have full control over the names for tables, fields, indexes and foreign key constraints in the mapped database schema.\n\n\u003e NOTE: Properties without a _node-sqlite3-orm_ decorator will not be mapped to the database.\n\n\u003c!-- --\u003e\n\n\u003e NOTE: you can use the 'temp' qualifier to create a temporary table. e.g `@table({name: 'temp.MYTEMPTABLE'`\n\n\u003c!-- --\u003e\n\n\u003e NOTE: you can map the same table to different model classes, e.g for using a partial model class\n\n## Database Connection\n\nSqlDatabase is a thin promised-based wrapper around sqlite3.Database: [node-sqlite3](https://github.com/TryGhost/node-sqlite3)\n\n```TypeScript\nimport {SqlDatabase} from 'sqlite3orm';\n\n(async () =\u003e {\n  let sqldb = new SqlDatabase();\n  // await sqldb.open(':memory:'); // would open a memory database in private mode\n  await sqldb.open('file:sqlite3orm?mode=memory\u0026cache=shared') // opens a memory database in shared mode\n})();\n```\n\n## Schema Creation\n\n```TypeScript\nimport {schema} from 'sqlite3orm';\n\n(async() =\u003e {\n  // get the user_version from the database:\n  let userVersion = await sqldb.getUserVersion();\n\n  // create all the tables if they do not exist:\n  await schema().createTable(sqldb, 'USERS');\n  await schema().createTable(sqldb, 'CONTACTS');\n  await schema().createIndex(sqldb, 'CONTACTS', 'idx_contacts_user');\n\n  if (userVersion \u003e= 1 \u0026\u0026 userVersion \u003c 10) {\n    // the 'CONTACTS' table has been introduced in user_version 1\n    // a column 'contact_mobile' has been added to the 'CONTACTS'\n    // table in user_version 10\n    await schema().alterTableAddColumn(\n        sqldb, 'CONTACTS', 'contact_mobile');\n  }\n  await sqldb.setUserVersion(10);\n})();\n```\n\n## Select/Insert/Update/Delete using DAOs\n\nIn order to read from or write to the database, you can use the `BaseDAO\u003cModel\u003e' class\n\n```TypeScript\n\n(async () =\u003e {\n\n  let userDAO = new BaseDAO(User, sqldb);\n  let contactDAO = new BaseDAO(Contact, sqldb);\n\n  // insert a user:\n  let user = new User();\n  user.userId = 1;\n  user.userLoginName = 'donald';\n  user.userJsonData = { lastScores: [10, 42, 31]};\n  user = await userDAO.insert(user);\n\n  // insert a contact:\n  let contact = new Contact();\n  contact.userId = 1;\n  contact.emailAddress = 'donald@duck.com';\n  contact = await contactDAO.insert(contact);\n\n  // update a contact:\n  contact.mobile = '+49 123 456';\n  contact = await contactDAO.update(contact);\n\n  // read a user:\n  let userDonald = await userDAO.select(user);\n\n  // update a user partially:\n  await userDAO.updatePartial({userId: userDonald.userId, deleted: true});\n\n  // read all contacts (child) for a given user (parent):\n  let contactsDonald1 = await contactDAO.selectAllOf('fk_user_contacts', User, userDonald);\n  //   or\n  let contactsDonald2 = await userDAO.selectAllChildsOf('fk_user_contacts', Contact, userDonald);\n\n  // read all users:\n  let allUsers = await userDAO.selectAll();\n\n  // read all users having login-name starting with 'd':\n  // (see section 'typesafe queries')\n  let selectedUsers = await userDAO.selectAll({userLoginName: {isLike: 'd%'});\n\n  // read all users having a contact:\n  let allUsersHavingContacts = await userDAO.selectAll(\n      'WHERE EXISTS(SELECT 1 FROM CONTACTS C WHERE C.user_id = T.user_id)');\n\n  // read all contacts from 'duck.com':\n  let allContactsFromDuckDotCom = await contactDAO.selectAll(\n      'WHERE contact_email like $contact_email',\n      {$contact_email: '%@duck.com'});\n\n  // read user (parent) for a given contact (child)\n  let userDonald1 = await userDAO.selectByChild('fk_user_contacts', Contact, contactsDonald1[0]);\n  // or\n  let userDonald2 = await contactDAO.selectParentOf('fk_user_contacts', User, contactsDonald2[0]);\n\n})();\n\n```\n\n## Typesafe query syntax\n\n### Filter\n\n```TypeScript\ninterface Filter\u003cModelType\u003e {\n  select?: Columns\u003cModelType\u003e;      // the columns which should be returned by the select\n  where?: Where\u003cModelType\u003e;         // the conditions for the WHERE-clause\n  order?: OrderColumns\u003cModelType\u003e;  // the columns to use for 'ORDER BY'-clause\n  limit?: number;                   // the limit for the 'LIMIT'-clause\n  offset?: number;                  // the offset for the 'LIMIT'-clause\n  tableAlias?: string;              // a table alias to use for the query\n}\n```\n\n#### select-object\n\nOnly columns mapped to properties that evaluate to true participate in the result set. Therefore, this select-object is only\nuseful for methods that return an array of partials and is otherwise ignored\n\n```TypeScript\nconst filter = {\n  select: {userId: true, userLoginName: true}\n}\n```\n\n#### where-object\n\nThe where-object consists of predicates and may be grouped by (nested conditions: 'and', 'or', 'not')\n\nA simple predicate is defined for a specific property of the model, the comparison operator and the value:\n\n```TypeScript\n{userLoginName: {eq: 'donald'}}   // transforms to: WHERE user_loginname = 'donald'\n```\n\nFor the 'eq' operator a shorthand form exist:\n\n```TypeScript\n{userLoginName: 'donald'}         // transforms to: WHERE user_loginname = 'donald'\n```\n\nAll the given values are not inserted directly into the SQL, but passed via parameters:\neg: 'WHERE user_loginname = :userLoginName'\n\nWe can define multiple predicates on one property as well as on multiple properties:\n\n```TypeScript\n{\n  userLoginName: {gte: 'A', lt: 'B' },\n  userJsonData: {isNotNull: true}\n}   // transforms to: WHERE user_loginname \u003e= 'A' AND user_loginname \u003c 'B' AND user_json IS NOT NULL\n```\n\nAll predicates are combined using logical 'AND' operator, so if we have the need for a logical 'OR' we would do something like that:\n\n```TypeScript\n{\n  or: [{deleted: true}, {deleted: {isNull: true}}]\n}   // transforms to: WHERE user_deleted = 1 OR user_deleted IS NULL\n```\n\n'and' and 'or' operators are expecing an array, the 'not' operator requires a single child-condition/predicates only\n\n```TypeScript\n{\n  not: {\n    or: [{deleted: true}, {deleted: {isNull: true}}]\n  }\n}   // transforms to: WHERE NOT ( user_deleted = 1 OR user_deleted IS NULL )\n```\n\nFurthermore, it is also possible to define parts of the query as sql expression, or replace the complete where-object with a sql where-clause:\n\n```TypeScript\n{\n  and: [{deleted: true}, {sql: `\nEXISTS (select 1 from CONTACTS C where C.user_id = T.user_id)\n  `}]\n}   // transforms to: WHERE NOT ( user_deleted = 1 OR user_deleted IS NULL )\n```\n\n\u003e NOTE: If you want to use user input as part of the sql expression, it is highly recommendet to use host variables instead. The value for the host variables can be defined using an additional and optional 'params' object\n\n#### additional filter properties and other things worth mentioning\n\n`limit` and `offset` speak for themselves.\nBy defining the `tableAlias` property, the default alias 'T' for the main table (see above) can be overwritten\n\nFor all 'update*' and 'delete*' methods, only the where-object part is needed, not the complete filter definition:\n\n```TypeScript\nuserDAO.deleteAll({deleted: true});\n```\n\n## Supported data types\n\nAll primitive JavaScript data types ('String', 'Number', 'Boolean') and properties of type 'Date' are supported.\nType safety is guaranteed, when reading properties of these types from the database (NULL values are treated as 'undefined').\n\n**Date** properties can be mapped to either the 'TEXT' or to the 'INTEGER' storage class (defaults to 'INTEGER') and their values will be stored as UTC. Using 'INTEGER' converts to Unix-Time, so fractions of seconds are lost. This can be changed by using the field option 'dateInMilliSeconds' or by setting as default using `schema().dateInMilliSeconds = true`.\n\nThese are the corresponding defaults for a 'current timestamp':\n\ndefault for 'TEXT':\n\n```TypeScript\n dbtype: 'TEXT    DEFAULT(datetime(\\'now\\') || \\'Z\\')'\n```\n\ndefault for 'INTEGER' (in seconds):\n\n```TypeScript\n dbtype: 'INTEGER DEFAULT(CAST(strftime(\\'%s\\',\\'now\\') as INT))' // unix epoch in seconds\n```\n\n**Boolean** properties can either be mapped to 'TEXT' or to 'INTEGER' (default). On storing a boolean value **false** will be converted to '0' and **true** will be converted to '1', on reading '0' or 'false' will be converted to **false** and '1' or 'true' will be converted to **true**. All other values will result in **undefined**\n\nOther data types can be serialized to a database field of type TEXT in JSON format, by setting the option 'isJson' to **true** (see sample above).\n\nadditional you have the possibility to apply your own serialze/deserialize functions by setting the 'transform' option.\n\n## Connection pool\n\n\u003e NOTE: For each database transaction, the involved database connection (SqlDatabase instance) should be used exclusively!\n\nOne possibility to achieve this could be to use a connection pool and to perform all database transactions with their own database connection.\n\n\u003e NOTE: instances of BaseDAO are lightweight objects and can be created on the fly and exclusively for one database transaction\n\n```TypeScript\n\n(async () =\u003e {\n  let pool = new SqlConnectionPool();\n\n  // open the database connection pool with 1 to 2 database connections:\n  await pool.open('/path/to/mydata.db', SQL_OPEN_DEFAULT, 1, 2);\n\n  let con1 = await pool.get();\n  let con2 = await pool.get();\n  await Promise.all([doSomeThing(con1), doAnotherThing(con2)]);\n\n  // free all connections to the pool:\n  con1.close();\n  pool.release(con2);\n\n})();\n\n```\n\n## Autoupgrade\n\nautomatically create or upgrade tables and indexes in the database based on your table definitions\n\n```TypeScript\nconst autoUpgrader = new AutoUpgrader(sqldb);\n\n// run autoupgrade for all registered tables:\nautoUpgrader.upgradeAllTables();\n\n// test if table definitions are up-to-date\nautoUpgrader.isActual([userDAO.table, contactDAO.table]);\n\n// run autoupgrade for specific table(s):\nautoUpgrader.upgradeTables([userDAO.table]);\n```\n\n\u003e NOTE: _autoupgrade_ should be carefully tested before running it on a production database! A backup should be self-evident!\n\n\u003c!-- --\u003e\n\n\u003e NOTE: renaming of columns can not be detected! _autoupgrade_ would normally add a new column with the new name and the data in the old column would be lost, but there is an option 'keepOldColumns' for preventing old columns from beeing dropped. Recycling the old column name for other purpose is asking for trouble\n\n\u003c!-- --\u003e\n\n\u003e NOTE: changing autoIncrement cannot be detected! You can use the optional parameter _force_ to force a recreation\n\n\u003c!-- --\u003e\n\n\u003e NOTE: if you have changed the column type, the table definition will be updated accordingly, but the content of the column will be still the same. You need an additional action if you want to convert the content of the column\n\n\u003c!-- --\u003e\n\n\u003e NOTE: please always add a DEFAULT-clause for newly added columns which are not nullable\n\n## explicit AUTOINCREMENT vs implicit AUTOINCREMENT\n\n[SQLite Autoincrement](https://www.sqlite.org/autoinc.html)\n\nWhenever you create a table without specifying either WITHOUT ROWID or AUTOINCREMENT, you get an implicit autoincrement column called ROWID.\n\nWhenever you create a table without specifying WITHOUT ROWID and having a PRIMARY KEY column of type INTEGER, this column is an alias of the ROWID column, which is always a 64-bit signed integer.\n\nOn an INSERT, if the ROWID or INTEGER PRIMARY KEY column is not explicitly given a value, then it will be filled automatically with an unused integer, usually one more than the largest ROWID currently in use. This is true regardless of whether or not the AUTOINCREMENT keyword is used.\n\nThe AUTOINCREMENT keyword imposes extra CPU, memory, disk space, and disk I/O overhead and should be avoided if not strictly needed. It is usually not needed.\n\nIf the AUTOINCREMENT keyword appears after INTEGER PRIMARY KEY, that changes the automatic ROWID assignment algorithm to prevent the reuse of ROWIDs over the lifetime of the database. In other words, the purpose of AUTOINCREMENT is to prevent the reuse of ROWIDs from previously deleted rows.\n\n### BaseDAO support for explicit and implicit AUTOINCREMENT\n\nfor historical reasons, when explicit AUTOINCREMENT is in use, the insert methods of the BaseDAO class by default prevents the insertion of predefined primary key values, so that the primary key column is always filled automatically with a generated ROWID. This is not the case, when implicit AUTOINCREMENT is in use.\n\nThis behavior can be overwritten globally using the static `BaseDAO.options` or using an optional parameter for the insert method:\n\n#### BaseDAO insert modes\n\n- StrictSqlite: use the provided value if defined, otherwise sqlite generates the value automatically\n- ForceAutoGeneration: prevents the insertion of predefined primary key values; always let sqlite generate a value automatically\n\n## Online Backup\n\nquick start:\n\n```Typescript\nimport {SqlDatabase} from 'sqlite3orm';\n\n(async () =\u003e {\n  const sqldb = new SqlDatabase();\n  await sqldb.open('file:sqlite3orm?mode=memory\u0026cache=shared')\n  ... add your tables\n\n  const backup = await sqldb.backup('backup.db');\n  await backup.step(-1);\n  backup.finish();\n})();\n```\n\n## Tracing\n\n**sqlite3orm** is using the 'debug' module to provide tracing functionality\nYou can turn on the logging by setting the 'DEBUG' environment to \"sqlite3orm:\\*\"\n\n## Install\n\n```bash\nnpm install sqlite3orm\n```\n\nWhen using TypeScript, the compiler options `experimentalDecorators` and `emitDecoratorMetadata` must be enabled.\n\ntsconfig.json:\n\n```JSON\n{\n  \"compilerOptions\": {\n    \"experimentalDecorators\": true,\n    \"emitDecoratorMetadata\": true,\n    ...\n  },\n  ...\n}\n```\n\n\u003e NOTE: SQLite's SQLCipher extension is also supported, see [SQLCipher](./docs/sqlcipher)\n\n\u003c!-- --\u003e\n\n\u003e NOTE: for custom builds and Electron, see [Custom builds and Electron](https://github.com/TryGhost/node-sqlite3#custom-builds-and-electron)\n\n## RELEASE NOTES\n\n[CHANGELOG](./CHANGELOG.md)\n\n## LICENSE\n\n**node-sqlite3-orm** is licensed under the MIT License: [LICENSE](./LICENSE)\n\n## WIKI\n\nfurther documentation can be found in our [Wiki](https://github.com/gms1/node-sqlite3-orm/wiki/)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgms1%2Fnode-sqlite3-orm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgms1%2Fnode-sqlite3-orm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgms1%2Fnode-sqlite3-orm/lists"}