{"id":13678899,"url":"https://github.com/alfateam/orange-orm","last_synced_at":"2026-01-18T14:18:19.351Z","repository":{"id":8665388,"uuid":"10321083","full_name":"alfateam/orange-orm","owner":"alfateam","description":"The ultimate ORM for Node and Typescript","archived":false,"fork":false,"pushed_at":"2026-01-16T14:07:19.000Z","size":6100,"stargazers_count":940,"open_issues_count":9,"forks_count":19,"subscribers_count":19,"default_branch":"master","last_synced_at":"2026-01-17T03:21:55.731Z","etag":null,"topics":["database","mssql","mssqlserver","mysql","nodejs","orm","orms","postgres","postgresql","rdb","sap","sql","sqlite","typescript"],"latest_commit_sha":null,"homepage":"https://orange-orm.io","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alfateam.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"docs/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":["lroal"]}},"created_at":"2013-05-27T19:20:34.000Z","updated_at":"2026-01-16T02:01:10.000Z","dependencies_parsed_at":"2023-12-19T17:57:37.196Z","dependency_job_id":"d9e4a5c1-7f2b-46b5-9198-c76aeb21613b","html_url":"https://github.com/alfateam/orange-orm","commit_stats":{"total_commits":1383,"total_committers":15,"mean_commits":92.2,"dds":"0.42371655820679677","last_synced_commit":"9f7cb462cb59c786a715fcc6e8e28c7dc77a7507"},"previous_names":["alfateam/orange-orm","alfateam/rdb","alfateam/a_sql"],"tags_count":78,"template":false,"template_full_name":null,"purl":"pkg:github/alfateam/orange-orm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alfateam%2Forange-orm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alfateam%2Forange-orm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alfateam%2Forange-orm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alfateam%2Forange-orm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alfateam","download_url":"https://codeload.github.com/alfateam/orange-orm/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alfateam%2Forange-orm/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28537502,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-18T13:04:05.990Z","status":"ssl_error","status_checked_at":"2026-01-18T13:01:44.092Z","response_time":98,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":["database","mssql","mssqlserver","mysql","nodejs","orm","orms","postgres","postgresql","rdb","sap","sql","sqlite","typescript"],"created_at":"2024-08-02T13:00:59.634Z","updated_at":"2026-01-18T14:18:19.321Z","avatar_url":"https://github.com/alfateam.png","language":"JavaScript","readme":"\u003cdiv align=\"center\"\u003e\n\u003cimg src=\"./docs/orange.svg\" alt=\"Orange ORM Logo\" width=\"250\"/\u003e\n\u003c/div\u003e\n\nThe ultimate Object Relational Mapper for Node.js, Bun and Deno, offering seamless integration with a variety of popular databases. Orange ORM supports both TypeScript and JavaScript, including both CommonJS and ECMAScript.  \n\n[![npm version](https://img.shields.io/npm/v/orange-orm.svg?style=flat-square)](https://www.npmjs.org/package/orange-orm)\n[![Build status](https://github.com/alfateam/orange-orm/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/alfateam/orange-orm/actions)\n[![Coverage Badge](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/lroal/1a69422f03da7f8155cf94fe66022452/raw/rdb__heads_master.json)](https://github.com/alfateam/orange-orm/actions)\n[![Github](https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/lroal/1ccb2b79abbe0258d636e9b5e4630a1a/raw/rdb__heads_master.json)](https://github.com/alfateam/orange-orm)\n[![GitHub Discussions](https://img.shields.io/github/discussions/alfateam/orange-orm)](https://github.com/alfateam/orange-orm/discussions)\n[![Discord](https://badgen.net/discord/online-members/QjuEgvQXzd?icon=discord\u0026label=Discord)](https://discord.gg/QjuEgvQXzd)\n[![YouTube Video Views](https://img.shields.io/youtube/views/1IwwjPr2lMs)](https://youtu.be/1IwwjPr2lMs)\n\n\n\n## Key Features \n\n- **Rich Querying Model**: Orange provides a powerful and intuitive querying model, making it easy to retrieve, filter, and manipulate data from your databases.\n- **Active Record**: With a concise and expressive syntax, Orange enables you to interact with your database using the [*Active Record Pattern*](https://en.wikipedia.org/wiki/Active_record_pattern).\n- **No Code Generation Required**: Enjoy full IntelliSense, even in table mappings, without the need for cumbersome code generation.\n- **TypeScript and JavaScript Support**: Orange fully supports both TypeScript and JavaScript, allowing you to leverage the benefits of static typing and modern ECMAScript features.\n- **Works in the Browser**: You can securely use Orange in the browser by utilizing the Express.js plugin, which serves to safeguard sensitive database credentials from exposure at the client level and protect against SQL injection. This method mirrors a traditional REST API, augmented with advanced TypeScript tooling for enhanced functionality.\n\n## Supported Databases and Runtimes\n|               | Node | Deno | Bun |Cloudflare | Web |\n| ------------- | :-----: | :-----: | :-----: | :-----: | :-----: | \n| Postgres      | ✅ | ✅ | ✅ | ✅|\n| PGlite      | ✅ | ✅ | ✅ | ✅ | ✅\n| MS SQL        | ✅ |  | ✅ | |\n| MySQL         | ✅ | ✅ | ✅ || \n| Oracle        | ✅ | ✅ | ✅ | |\n| SAP ASE       | ✅ |  |  | |\n| SQLite        | ✅ | ✅ | ✅ | |\n| Cloudflare D1 |  |  |  | ✅|\n\nThis is the _Modern Typescript Documentation_. Are you looking for the [_Classic Documentation_](https://github.com/alfateam/orange-orm/blob/master/docs/docs.md) ?\n\n## Sponsorship \u003cspan style=\"font-size: larger; color: darkred;\"\u003e♡\u003c/span\u003e\nIf you value the hard work behind Orange and wish to see it evolve further, consider [sponsoring](https://github.com/sponsors/lroal). Your support fuels the journey of refining and expanding this tool for our developer community.\n\n## Installation\n\n```bash\nnpm install orange-orm\n```  \n\n## Example\nWatch the [tutorial video on YouTube](https://youtu.be/1IwwjPr2lMs)\n\n![Relations diagram](./docs/diagram.svg)  \n\n\u003csub\u003e📄 map.ts\u003c/sub\u003e\n```javascript\nimport orange from 'orange-orm';\n\nconst map = orange.map(x =\u003e ({\n  customer: x.table('customer').map(({ column }) =\u003e ({\n    id: column('id').numeric().primary().notNullExceptInsert(),\n    name: column('name').string(),\n    balance: column('balance').numeric(),\n    isActive: column('isActive').boolean(),\n  })),\n\n  order: x.table('_order').map(({ column }) =\u003e ({\n    id: column('id').numeric().primary().notNullExceptInsert(),\n    orderDate: column('orderDate').date().notNull(),\n    customerId: column('customerId').numeric().notNullExceptInsert(),\n  })),\n\n  orderLine: x.table('orderLine').map(({ column }) =\u003e ({\n    id: column('id').numeric().primary(),\n    orderId: column('orderId').numeric(),\n    product: column('product').string(),\n    amount: column('amount').numeric(),\n  })),\n\n  package: x.table('package').map(({ column }) =\u003e ({\n    id: column('packageId').numeric().primary().notNullExceptInsert(),\n    lineId: column('lineId').numeric().notNullExceptInsert(),\n    sscc: column('sscc').string() //the barcode\n  })),\n\n  deliveryAddress: x.table('deliveryAddress').map(({ column }) =\u003e ({\n    id: column('id').numeric().primary(),\n    orderId: column('orderId').numeric(),\n    name: column('name').string(),\n    street: column('street').string(),\n    postalCode: column('postalCode').string(),\n    postalPlace: column('postalPlace').string(),\n    countryCode: column('countryCode').string(),\n  }))\n\n})).map(x =\u003e ({\n  orderLine: x.orderLine.map(({ hasMany }) =\u003e ({\n    packages: hasMany(x.package).by('lineId')\n  })),\n  order: x.order.map(v =\u003e ({\n    customer: v.references(x.customer).by('customerId'),\n    lines: v.hasMany(x.orderLine).by('orderId'),\n    deliveryAddress: v.hasOne(x.deliveryAddress).by('orderId'),\n  }))\n}));\n\nexport default map;\n```  \n\u003csub\u003e📄 update.ts\u003c/sub\u003e\n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\nupdateRow();\n\nasync function updateRow() {\n  const order = await db.order.getById(2, {\n    lines: true\n  });\n  order.lines.push({\n    product: 'broomstick',\n    amount: 300\n  });\n\n  await order.saveChanges();\n}\n\n```\n\u003csub\u003e📄 filter.ts\u003c/sub\u003e\n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const orders = await db.order.getAll({\n    where: x =\u003e x.lines.any(line =\u003e line.product.contains('broomstick'))\n      .and(x.customer.name.startsWith('Harry')),\n    lines: {\n      packages: true\n    },\n    deliveryAddress: true,    \n    customer: true\n  });  \n}\n\n```\n\n## API \n\n\u003cdetails id=\"table-mapping\"\u003e\u003csummary\u003e\u003cstrong\u003eMapping tables\u003c/strong\u003e\u003c/summary\u003e\n\u003cp\u003eTo define a mapping, you employ the \u003cstrong\u003e\u003ci\u003emap()\u003c/i\u003e\u003c/strong\u003e method, linking your tables and columns to corresponding object properties. You provide a callback function that engages with a parameter representing a database table.\n\nEach column within your database table is designated by using the \u003cstrong\u003e\u003ci\u003ecolumn()\u003c/i\u003e\u003c/strong\u003e method, in which you specify its name. This action generates a reference to a column object that enables you to articulate further column properties like its data type or if it serves as a primary key.\n\nRelationships between tables can also be outlined. By using methods like \u003cstrong\u003e\u003ci\u003ehasOne\u003c/i\u003e\u003c/strong\u003e, \u003cstrong\u003e\u003ci\u003ehasMany\u003c/i\u003e\u003c/strong\u003e, and \u003cstrong\u003e\u003ci\u003ereferences\u003c/i\u003e\u003c/strong\u003e, you can establish connections that reflect the relationships in your data schema. In the example below, an 'order' is linked to a 'customer' reference, a 'deliveryAddress', and multiple 'lines'. The hasMany and hasOne relations represents ownership - the tables 'deliveryAddress' and 'orderLine' are owned by the 'order' table, and therefore, they contain the 'orderId' column referring to their parent table, which is 'order'. The similar relationship exists between orderLine and package - hence the packages are owned by the orderLine. Conversely, the customer table is independent and can exist without any knowledge of the 'order' table. Therefore we say that the order table \u003ci\u003ereferences\u003c/i\u003e the customer table - necessitating the existence of a 'customerId' column in the 'order' table.\u003c/p\u003e\n\n\u003csub\u003e📄 map.ts\u003c/sub\u003e\n```javascript\nimport orange from 'orange-orm';\n\nconst map = orange.map(x =\u003e ({\n  customer: x.table('customer').map(({ column }) =\u003e ({\n    id: column('id').numeric().primary().notNullExceptInsert(),\n    name: column('name').string(),\n    balance: column('balance').numeric(),\n    isActive: column('isActive').boolean(),\n  })),\n\n  order: x.table('_order').map(({ column }) =\u003e ({\n    id: column('id').numeric().primary().notNullExceptInsert(),\n    orderDate: column('orderDate').date().notNull(),\n    customerId: column('customerId').numeric().notNullExceptInsert(),\n  })),\n\n  orderLine: x.table('orderLine').map(({ column }) =\u003e ({\n    id: column('id').numeric().primary(),\n    orderId: column('orderId').numeric(),\n    product: column('product').string(),\n  })),\n\n  package: x.table('package').map(({ column }) =\u003e ({\n    id: column('packageId').numeric().primary().notNullExceptInsert(),\n    lineId: column('lineId').numeric().notNullExceptInsert(),\n    sscc: column('sscc').string() //the barcode\n  })),\n\n  deliveryAddress: x.table('deliveryAddress').map(({ column }) =\u003e ({\n    id: column('id').numeric().primary(),\n    orderId: column('orderId').numeric(),\n    name: column('name').string(),\n    street: column('street').string(),\n    postalCode: column('postalCode').string(),\n    postalPlace: column('postalPlace').string(),\n    countryCode: column('countryCode').string(),\n  }))\n\n})).map(x =\u003e ({\n  orderLine: x.orderLine.map(({ hasMany }) =\u003e ({\n    packages: hasMany(x.package).by('lineId')\n  })),\n  order: x.order.map(({ hasOne, hasMany, references }) =\u003e ({\n    customer: references(x.customer).by('customerId'),\n    deliveryAddress: hasOne(x.deliveryAddress).by('orderId'),\n    lines: hasMany(x.orderLine).by('orderId')\n  }))\n}));\n\nexport default map;\n```\nThe init.ts script resets our SQLite database. It's worth noting that SQLite databases are represented as single files, which makes them wonderfully straightforward to manage.\n\nAt the start of the script, we import our database mapping from the map.ts file. This gives us access to the db object, which we'll use to interact with our SQLite database.\n\nThen, we define a SQL string. This string outlines the structure of our SQLite database. It first specifies to drop existing tables named 'deliveryAddress', 'package', 'orderLine', '_order', and 'customer' if they exist. This ensures we have a clean slate. Then, it dictates how to create these tables anew with the necessary columns and constraints.\n\nBecause of a peculiarity in SQLite, which only allows one statement execution at a time, we split this SQL string into separate statements. We do this using the split() method, which breaks up the string at every semicolon.  \n\n\u003csub\u003e📄 init.ts\u003c/sub\u003e\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\nconst sql = `DROP TABLE IF EXISTS deliveryAddress;\nDROP TABLE IF EXISTS package;\nDROP TABLE IF EXISTS orderLine;\nDROP TABLE IF EXISTS _order;\nDROP TABLE IF EXISTS customer;\n\nCREATE TABLE customer (\n    id INTEGER PRIMARY KEY,\n    name TEXT,\n    balance NUMERIC,\n    isActive INTEGER\n);\n\nCREATE TABLE _order (\n    id INTEGER PRIMARY KEY,\n    orderDate TEXT,\n    customerId INTEGER REFERENCES customer\n);\n\nCREATE TABLE orderLine (\n    id INTEGER PRIMARY KEY,\n    orderId INTEGER REFERENCES _order,\n    product TEXT,\n    amount NUMERIC(10,2)\n);\n\nCREATE TABLE package (\n    packageId INTEGER PRIMARY KEY,\n    lineId INTEGER REFERENCES orderLine,\n    sscc TEXT\n);\n\nCREATE TABLE deliveryAddress (\n    id INTEGER PRIMARY KEY,\n    orderId INTEGER REFERENCES _order,\n    name TEXT, \n    street TEXT,\n    postalCode TEXT,\n    postalPlace TEXT,\n    countryCode TEXT\n)\n`;\n\n\nasync function init() {\n  const statements = sql.split(';');\n  for (let i = 0; i \u003c statements.length; i++) {\n    await db.query(statements[i]);\n  }\n}\nexport default init;\n```\nIn SQLite, columns with the INTEGER PRIMARY KEY attribute are designed to autoincrement by default. This means that each time a new record is inserted into the table, SQLite automatically produces a numeric key for the id column that is one greater than the largest existing key. This mechanism is particularly handy when you want to create unique identifiers for your table rows without manually entering each id.\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eConnecting\u003c/strong\u003e\u003c/summary\u003e\n\n__SQLite__  \nWhen running **Node.js 21 and earlier**, you need to install the `sqlite3` dependency.  \nWhen running Node.js 22 and later, Bun, or Deno,  you don't need it as it is built-in.  \n```bash\nnpm install sqlite3\n```  \n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n// … use the database …\n\n// IMPORTANT for serverless functions:\nawait db.close();           // closes the client connection\n```\n__With connection pool__\n```bash\nnpm install sqlite3\n```  \n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db', { size: 10 });\n// … use the pool …\n\n// IMPORTANT for serverless functions:\nawait pool.close();         // closes all pooled connections\n```\n__Why close ?__  \nIn serverless environments (e.g. AWS Lambda, Vercel, Cloudflare Workers) execution contexts are frequently frozen and resumed. Explicitly closing the client or pool ensures that file handles are released promptly and prevents “database locked” errors between invocations.  \n\n__From the browser__  \nYou can securely use Orange from the browser by utilizing the Express plugin, which serves to safeguard sensitive database credentials from exposure at the client level. This technique bypasses the need to transmit raw SQL queries directly from the client to the server. Instead, it logs method calls initiated by the client, which are later replayed and authenticated on the server. This not only reinforces security by preventing the disclosure of raw SQL queries on the client side but also facilitates a smoother operation. Essentially, this method mirrors a traditional REST API, augmented with advanced TypeScript tooling for enhanced functionality. You can read more about it in the section called [In the browser](#user-content-in-the-browser)  \n\u003csub\u003e📄 server.ts\u003c/sub\u003e\n```javascript\nimport map from './map';\nimport { json } from 'body-parser';\nimport express from 'express';\nimport cors from 'cors';\n\nconst db = map.sqlite('demo.db');\n\nexpress().disable('x-powered-by')\n  .use(json({ limit: '100mb' }))\n  .use(cors())\n  //for demonstrational purposes, authentication middleware is not shown here.\n  .use('/orange', db.express())\n  .listen(3000, () =\u003e console.log('Example app listening on port 3000!'));\n```\n\n\u003csub\u003e📄 browser.ts\u003c/sub\u003e\n```javascript\nimport map from './map';\n\nconst db = map.http('http://localhost:3000/orange');\n```\n\n__MySQL__\n```bash\n$ npm install mysql2\n```  \n```javascript\nimport map from './map';\nconst db = map.mysql('mysql://test:test@mysql/test');\n```\n\n\n__MS SQL__\n```bash\nnpm install tedious\n```  \n```javascript\nimport map from './map';\nconst db = map.mssql({\n          server: 'mssql',\n          options: {\n            encrypt: false,\n            database: 'test'\n          },\n          authentication: {\n            type: 'default',\n            options: {\n              userName: 'sa',\n              password: 'P@assword123',\n            }\n          }\n        });\n```\n\n__PostgreSQL__  \nWith Bun, you don't need to install the `pg` package as PostgreSQL support is built-in.\n```bash\nnpm install pg\n```  \n```javascript\nimport map from './map';\nconst db = map.postgres('postgres://postgres:postgres@postgres/postgres');\n```\nWith schema\n```javascript\nimport map from './map';\nconst db = map.postgres('postgres://postgres:postgres@postgres/postgres?search_path=custom');\n```\n__PGlite__  \n```bash\nnpm install @electric-sql/pglite\n```  \nIn this example we use the in-memory Postgres.  \nRead more about [PGLite connection configs](https://pglite.dev/docs/).  \n```javascript\nimport map from './map';\nconst db = map.pglite( /* config? : PGliteOptions */);\n```\n__Cloudflare D1__  \n\u003csub\u003e📄 wrangler.toml\u003c/sub\u003e  \n```toml\nname = \"d1-tutorial\"\nmain = \"src/index.ts\"\ncompatibility_date = \"2025-02-04\"\n\n# Bind a D1 database. D1 is Cloudflare’s native serverless SQL database.\n# Docs: https://developers.cloudflare.com/workers/wrangler/configuration/#d1-databases\n[[d1_databases]]\nbinding = \"DB\"\ndatabase_name = \"\u003cyour-name-for-the-database\u003e\"\ndatabase_id = \"\u003cyour-guid-for-the-database\u003e\"\n```\n\n\u003csub\u003e📄 src/index.ts\u003c/sub\u003e  \n```javascript\nimport map from './map';\n\nexport interface Env {\n  // Must match the binding name in wrangler.toml  \n  DB: D1Database;\n}\n\nexport default {\n  async fetch(request, env): Promise\u003cResponse\u003e {\n    const db = map.d1(env.DB);\n    const customers = await db.customer.getAll();\n    return Response.json(customers);\n  },\n} satisfies ExportedHandler\u003cEnv\u003e;\n```\n__Oracle__\n```bash\nnpm install oracledb\n```  \n```javascript\nimport map from './map';\nconst db = map.oracle({\n  user: 'sys',\n  password: 'P@assword123',\n  connectString: 'oracle/XE',\n  privilege: 2\n});\n```\n__SAP Adaptive Server__  \nEven though msnodesqlv8 was developed for MS SQL, it also works for SAP ASE as it is ODBC compliant.  \n```bash\nnpm install msnodesqlv8\n```  \n```javascript\nimport { fileURLToPath } from 'url';\nimport { dirname } from 'path';\nimport map from './map';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n//download odbc driver from sap web pages\nconst db = map.sap(`Driver=${__dirname}/libsybdrvodb.so;SERVER=sapase;Port=5000;UID=sa;PWD=sybase;DATABASE=test`);\n\n```\n\n\u003c/details\u003e\n\n\u003cdetails id=\"inserting-rows\"\u003e\u003csummary\u003e\u003cstrong\u003eInserting rows\u003c/strong\u003e\u003c/summary\u003e\n\n\u003cp\u003eIn the code below, we initially import the table-mapping feature \"map.ts\" and the setup script \"init.ts\", both of which were defined in the preceding step. The setup script executes a raw query that creates the necessary tables. Subsequently, we insert two customers, named \"George\" and \"Harry\", into the customer table, and this is achieved through calling \"db.customer.insert\".\n\nNext, we insert an array of two orders in the order table. Each order contains an orderDate, customer information, deliveryAddress, and lines for the order items. We use the customer constants \"george\" and \"harry\" from previous inserts. Observe that we don't pass in any primary keys. This is because all tables here have autoincremental keys. The second argument to \"db.order.insert\" specifies a fetching strategy. This fetching strategy plays a critical role in determining the depth of the data retrieved from the database after insertion. The fetching strategy specifies which associated data should be retrieved and included in the resulting orders object. In this case, the fetching strategy instructs the database to retrieve the customer, deliveryAddress, and lines for each order.\n\nWithout a fetching strategy, \"db.order.insert\" would only return the root level of each order. In that case you would only get the id, orderDate, and customerId for each order.\u003c/p\u003e\n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\nimport init from './init';\n\ninsertRows();\n\nasync function insertRows() {\n  await init();\n\n  const george = await db.customer.insert({\n    name: 'George',\n    balance: 177,\n    isActive: true\n  });\n\n  const harry = await db.customer.insert({\n    name: 'Harry',\n    balance: 200,\n    isActive: true\n  });\n\n  const orders = await db.order.insert([\n    {\n      orderDate: new Date(2022, 0, 11, 9, 24, 47),\n      customer: george,\n      deliveryAddress: {\n        name: 'George',\n        street: 'Node street 1',\n        postalCode: '7059',\n        postalPlace: 'Jakobsli',\n        countryCode: 'NO'\n      },\n      lines: [\n        { product: 'Bicycle', amount: 250 },\n        { product: 'Small guitar', amount: 150 }\n      ]\n    },\n    {\n      customer: harry,\n      orderDate: new Date(2021, 0, 11, 12, 22, 45),\n      deliveryAddress: {\n        name: 'Harry Potter',\n        street: '4 Privet Drive, Little Whinging',\n        postalCode: 'GU4',\n        postalPlace: 'Surrey',\n        countryCode: 'UK'\n      },\n      lines: [\n        { product: 'Magic wand', amount: 300 }\n      ]\n    }\n  ], {customer: true, deliveryAddress: true, lines: true}); //fetching strategy\n}\n```\n\n__Conflict resolution__  \nBy default, the strategy for inserting rows is set to an optimistic approach. In this case, if a row is being inserted with an already existing primary key, the database raises an exception.\n\nCurrently, there are three concurrency strategies:\n- \u003cstrong\u003e`optimistic`\u003c/strong\u003e Raises an exception if another row was already inserted on that primary key.\n- \u003cstrong\u003e`overwrite`\u003c/strong\u003e Overwrites the property, regardless of changes by others.\n- \u003cstrong\u003e`skipOnConflict`\u003c/strong\u003e Silently avoids updating the property if another user has modified it in the interim.\n\nThe \u003cstrong\u003econcurrency\u003c/strong\u003e option can be set either for the whole table or individually for each column. In the example below, we've set the concurrency strategy on \u003cstrong\u003evendor\u003c/strong\u003e table to \u003cstrong\u003eoverwrite\u003c/strong\u003e except for the column \u003cstrong\u003ebalance\u003c/strong\u003e which uses the \u003cstrong\u003eskipOnConflict\u003c/strong\u003e strategy.  In this particular case, a row with \u003cstrong\u003eid: 1\u003c/strong\u003e already exists, the \u003cstrong\u003ename\u003c/strong\u003e and \u003cstrong\u003eisActive\u003c/strong\u003e fields will be overwritten, but the balance will remain the same as in the original record, demonstrating the effectiveness of combining multiple \u003cstrong\u003econcurrency\u003c/strong\u003e strategies.\n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ninsertRows();\n\nasync function insertRows() {\n\n  db2 = db({\n    vendor: {\n      balance: {\n        concurrency: 'skipOnConflict'\n      },\n      concurrency: 'overwrite'\n    }\n  });\n\n  await db2.vendor.insert({\n    id: 1,\n    name: 'John',\n    balance: 100,\n    isActive: true\n  });\n\n  //this will overwrite all fields but balance\n  const george = await db2.vendor.insert({\n    id: 1,\n    name: 'George',\n    balance: 177,\n        isActive: false\n  });\n  console.dir(george, {depth: Infinity});\n  // {\n  //   id: 1,\n  //   name: 'George',\n  //   balance: 100,\n  //   isActive: false\n  // }\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eFetching rows\u003c/strong\u003e\u003c/summary\u003e\n\u003cp\u003eOrange has a rich querying model. As you navigate through, you'll learn about the various methods available to retrieve data from your tables, whether you want to fetch all rows, many rows with specific criteria, or a single row based on a primary key.  \n\nThe fetching strategy in Orange is optional, and its use is influenced by your specific needs. You can define the fetching strategy either on the table level or the column level. This granularity gives you the freedom to decide how much related data you want to pull along with your primary request.\u003c/p\u003e\n\n__All rows__\n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const orders = await db.order.getAll({\n    customer: true, \n    deliveryAddress: true, \n    lines: {\n      packages: true\n    }\n  });\n}\n```\n__Limit, offset and order by__  \nThis script demonstrates how to fetch orders with customer, lines, packages and deliveryAddress, limiting the results to 10, skipping the first row, and sorting the data based on the orderDate in descending order followed by id. The lines are sorted by product.  \n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const orders = await db.order.getAll({\n    offset: 1,\n    orderBy: ['orderDate desc', 'id'],\n    limit: 10,\n    customer: true, \n    deliveryAddress: true, \n    lines: {\n      packages: true,\n      orderBy: 'product'\n    },\n  });\n}\n```\n\u003ca name=\"aggregate-results\"\u003e  \u003c/a\u003e\n__With aggregated results__  \nYou can count records and aggregate numerical columns. \nThe following operators are supported:\n- count\n- sum\n- min \n- max  \n- avg  \n\nYou can also elevate associated data to a parent level for easier access. In the example below, \u003ci\u003ebalance\u003c/i\u003e of the customer is elevated to the root level.\n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const orders = await db.order.getAll({\n    numberOfLines: x =\u003e x.count(x =\u003e x.lines.id),\n    totalAmount: x =\u003e x.sum(x =\u003e lines.amount),\n    balance: x =\u003e x.customer.balance\n  });\n}\n```\n\n__Many rows filtered__\n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const orders = await db.order.getAll({\n    where: x =\u003e x.lines.any(line =\u003e line.product.contains('i'))\n      .and(x.customer.balance.greaterThan(180)),\n    customer: true, \n    deliveryAddress: true, \n    lines: true\n  });\n}\n```\nYou can also use the alternative syntax for the `where-filter`. This way, the filter can be constructed independently from the fetching strategy. Keep in mind that you must use the `getMany` method instead of the `getAll` method.  \nIt is also possible to combine `where-filter` with the independent filter when using the `getMany` method.  \n```javascript\nasync function getRows() {\n  const filter = db.order.lines.any(line =\u003e line.product.contains('i'))\n                 .and(db.order.customer.balance.greaterThan(180));\n  const orders = await db.order.getMany(filter, {\n    //where: x =\u003e ... can be combined as well\n    customer: true, \n    deliveryAddress: true, \n    lines: true\n  });\n}\n```\n\n__Single row filtered__\n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const order = await db.order.getOne(undefined /* optional filter */, {\n    where: x =\u003e x.customer(customer =\u003e customer.isActive.eq(true)\n                 .and(customer.startsWith('Harr'))),\n    customer: true, \n    deliveryAddress: true, \n    lines: true\n  });\n}\n```\nYou can use also the alternative syntax for the `where-filter`. This way, the filter can be constructed independently from the fetching strategy.    \nIt is also possible to combine `where-filter` with the independent filter when using the `getOne` method.  \n```javascript\nasync function getRows() {\n  const filter = db.order.customer(customer =\u003e customer.isActive.eq(true)\n                 .and(customer.startsWith('Harr')));\n                 //equivalent, but creates slighly different sql:\n                 // const filter = db.order.customer.isActive.eq(true).and(db.order.customer.startsWith('Harr'));\n  const order = await db.order.getOne(filter, {\n    customer: true, \n    deliveryAddress: true, \n    lines: true\n  });\n}\n```\n\n__Single row by primary key__\n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const order = await db.order.getById(1, {\n    customer: true, \n    deliveryAddress: true, \n    lines: true\n  });\n}\n```\n\n__Many rows by primary key__\n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const orders = await db.order.getMany([\n      {id: 1},\n      {id: 2}\n    ], \n    {\n      customer: true, \n      deliveryAddress: true, \n      lines: true\n  });\n}\n```\n\u003c/details\u003e  \n\n\u003cdetails id=\"updating-rows\"\u003e\u003csummary\u003e\u003cstrong\u003eUpdating rows\u003c/strong\u003e\u003c/summary\u003e\n\u003cp\u003eTo update rows, modify the property values and invoke the method \u003cstrong\u003e\u003ci\u003esaveChanges()\u003c/i\u003e\u003c/strong\u003e. The function updates only the modified columns, not the entire row. Rows in child relations can also be updated as long as the parent order \u003ci\u003eowns\u003c/i\u003e the child tables. In our illustration, the \u003cstrong\u003eorder\u003c/strong\u003e table owns both the \u003cstrong\u003edeliveryAddress\u003c/strong\u003e and the \u003cstrong\u003elines\u003c/strong\u003e tables because they're part of a \u003ci\u003ehasOne/hasMany relationship\u003c/i\u003e. Contrastingly, the \u003cstrong\u003ecustomer\u003c/strong\u003e is part of a \u003ci\u003ereference relationship\u003c/i\u003e and thus can't be updated here. But you can detach the reference to the customer by assigning it to null or undefined. (Setting order.customerId to null or undefined achieves the same result.)\u003c/p\u003e\n\n__Updating a single row__\n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\nupdate();\n\nasync function update() {\n  const order = await db.order.getById(1, {\n    customer: true, \n    deliveryAddress: true, \n    lines: true\n  });\n\n  order.orderDate = new Date();\n  order.deliveryAddress = null;\n  order.lines.push({product: 'Cloak of invisibility', amount: 600});\n\n  await order.saveChanges();\n}\n```\n__Updating many rows__\n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\nupdate();\n\nasync function update() {\n  let orders = await db.order.getAll({\n    orderBy: 'id',\n    lines: true, \n    deliveryAddress: true, \n    customer: true\n  });\n\n  orders[0].orderDate = new Date();\n  orders[0].deliveryAddress.street = 'Node street 2';\n  orders[0].lines[1].product = 'Big guitar';\n\n  orders[1].orderDate = '2023-07-14T12:00:00'; //iso-string is allowed\n  orders[1].deliveryAddress = null;\n  orders[1].customer = null;\n  orders[1].lines.push({product: 'Cloak of invisibility', amount: 600});\n\n  await orders.saveChanges();\n}\n```\n__Selective updates__  \nThe update method is ideal for updating specific columns and relationships across one or multiple rows. You must provide a where filter to specify which rows to target. If you include a fetching strategy, the affected rows and their related data will be returned; otherwise, no data is returned.\n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\nupdate();\n\nasync function update() {\n\n  const propsToBeModified = {\n    orderDate: new Date(),\n    customerId: 2,\n    lines: [\n      { id: 1, product: 'Bicycle', amount: 250 }, //already existing line\n      { id: 2, product: 'Small guitar', amount: 150 }, //already existing line\n      { product: 'Piano', amount: 800 } //the new line to be inserted\n    ]\n  };\n\n  const strategy = {customer: true, deliveryAddress: true, lines: true};\n  const orders = await db.order.update(propsToBeModified, { where: x =\u003e x.id.eq(1) }, strategy);\n}\n```\n__Replacing a row from JSON__  \nThe replace method is suitable when a complete overwrite is required from a JSON object - typically in a REST API. However, it's important to consider that this method replaces the entire row and it's children, which might not always be desirable in a multi-user environment.\n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\nreplace();\n\nasync function replace() {\n\n  const modified = {\n    id: 1,\n    orderDate: '2023-07-14T12:00:00',\n    customer: {\n      id: 2\n    },\n    deliveryAddress: {\n      name: 'Roger', //modified name\n      street: 'Node street 1',\n      postalCode: '7059',\n      postalPlace: 'Jakobsli',\n      countryCode: 'NO'\n    },\n    lines: [\n      { id: 1, product: 'Bicycle', amount: 250 },\n      { id: 2, product: 'Small guitar', amount: 150 },\n      { product: 'Piano', amount: 800 } //the new line to be inserted\n    ]\n  };\n\n  const order = await db.order.replace(modified, {customer: true, deliveryAddress: true, lines: true});\n}\n```\n__Partially updating from JSON__  \n The updateChanges method applies a partial update based on difference between original and modified row. It is often preferable because it minimizes the risk of unintentionally overwriting data that may have been altered by other users in the meantime. To do so, you need to pass in the original row object before modification as well.\n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\nupdate();\n\nasync function update() {\n\n  const original = {\n    id: 1,\n    orderDate: '2023-07-14T12:00:00',\n    customer: {\n      id: 2\n    },\n    deliveryAddress: {\n      id: 1,\n      name: 'George',\n      street: 'Node street 1',\n      postalCode: '7059',\n      postalPlace: 'Jakobsli',\n      countryCode: 'NO'\n    },\n    lines: [\n      { id: 1, product: 'Bicycle', amount: 250 },\n      { id: 2, product: 'Small guitar', amount: 150 }\n    ]\n  };\n\n  const modified = JSON.parse(JSON.stringify(original));\n  modified.deliveryAddress.name = 'Roger';\n  modified.lines.push({ product: 'Piano', amount: 800 });\n\n  const order = await db.order.updateChanges(modified, original, { customer: true, deliveryAddress: true, lines: true });\n}\n```\n__Conflict resolution__  \nRows get updated using an \u003ci id=\"conflicts\"\u003eoptimistic\u003c/i\u003e concurrency approach by default. This means if a property being edited was meanwhile altered, an exception is raised, indicating the row was modified by a different user. You can change the concurrency strategy either at the table or column level.\n\nCurrently, there are three concurrency strategies:\n- \u003cstrong\u003e`optimistic`\u003c/strong\u003e Raises an exception if another user changes the property during an update.\n- \u003cstrong\u003e`overwrite`\u003c/strong\u003e Overwrites the property, regardless of changes by others.\n- \u003cstrong\u003e`skipOnConflict`\u003c/strong\u003e Silently avoids updating the property if another user has modified it in the interim.\n\nIn the example below, we've set the concurrency strategy for orderDate to 'overwrite'. This implies that if other users modify orderDate while you're making changes, their updates will be overwritten.\n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\nupdate();\n\nasync function update() {\n  const order = await db.order.getById(1, {\n    customer: true, \n    deliveryAddress: true, \n    lines: true\n  });\n\n  order.orderDate = new Date();\n  order.deliveryAddress = null;\n  order.lines.push({product: 'Cloak of invisibility',  amount: 600});\n\n  await order.saveChanges( {\n    orderDate: {\n      concurrency: 'overwrite'\n  }});\n}\n```\n\u003c/details\u003e  \n\n\u003cdetails id=\"upserting-rows\"\u003e\u003csummary\u003e\u003cstrong\u003eUpserting rows\u003c/strong\u003e\u003c/summary\u003e\nIt is possible to perform 'upserts' by taking advantage of the 'overwrite' strategy.\n\nCurrently, there are three concurrency strategies:\n- \u003cstrong\u003e`optimistic`\u003c/strong\u003e Raises an exception if another row was already inserted on that primary key.\n- \u003cstrong\u003e`overwrite`\u003c/strong\u003e Overwrites the property, regardless of changes by others.\n- \u003cstrong\u003e`skipOnConflict`\u003c/strong\u003e Silently avoids updating the property if another user has modified it in the interim.\n\nThe \u003cstrong\u003econcurrency\u003c/strong\u003e option can be set either for the whole table or individually for each column. In the example below, we've set the concurrency strategy on \u003cstrong\u003evendor\u003c/strong\u003e table to \u003cstrong\u003eoverwrite\u003c/strong\u003e except for the column \u003cstrong\u003ebalance\u003c/strong\u003e which uses the \u003cstrong\u003eskipOnConflict\u003c/strong\u003e strategy.  In this particular case, a row with \u003cstrong\u003eid: 1\u003c/strong\u003e already exists, the \u003cstrong\u003ename\u003c/strong\u003e and \u003cstrong\u003eisActive\u003c/strong\u003e fields will be overwritten, but the balance will remain the same as in the original record, demonstrating the effectiveness of combining multiple \u003cstrong\u003econcurrency\u003c/strong\u003e strategies.\n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ninsertRows();\n\nasync function insertRows() {\n\n  db2 = db({\n    vendor: {\n      balance: {\n        concurrency: 'skipOnConflict'\n      },\n      concurrency: 'overwrite'\n    }\n  });\n\n  await db2.vendor.insert({\n    id: 1,\n    name: 'John',\n    balance: 100,\n    isActive: true\n  });\n\n  //this will overwrite all fields but balance\n  const george = await db2.vendor.insert({\n    id: 1,\n    name: 'George',\n    balance: 177,\n        isActive: false\n  });\n  console.dir(george, {depth: Infinity});\n  // {\n  //   id: 1,\n  //   name: 'George',\n  //   balance: 100,\n  //   isActive: false\n  // }\n}\n```\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eDeleting rows\u003c/strong\u003e\u003c/summary\u003e\n\u003cp\u003eRows in owner tables cascade deletes to their child tables. In essence, if a table has ownership over other tables through \u003cstrong\u003e\u003ci\u003ehasOne\u003c/i\u003e\u003c/strong\u003e and \u003cstrong\u003e\u003ci\u003ehasMany\u003c/i\u003e\u003c/strong\u003e relationships, removing a record from the parent table also removes its corresponding records in its child tables. This approach safeguards against leaving orphaned records and upholds data integrity. On the contrary, tables that are merely referenced, through \u003cstrong\u003e\u003ci\u003ereference relationships \u003c/i\u003e\u003c/strong\u003e , remain unaffected upon deletions. For a deeper dive into these relationships and behaviors, refer to the section on \u003ca href=\"#user-content-table-mapping\"\u003eMapping tables\u003c/a\u003e.\u003c/p\u003e\n\n__Deleting a single row__\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ndeleteRow();\n\nasync function deleteRow() {    \n  const order = await db.order.getById(1);\n\n  await order.delete();\n  //will also delete deliveryAddress and lines\n  //but not customer\n}\n```\n__Deleting a row in an array__  \nA common workflow involves retrieving multiple rows, followed by the need to delete a specific row from an array. This operation is straightforward to do with Orange, which allow for the updating, inserting, and deleting of multiple rows in a single transaction. To modify the array, simply add, update, or remove elements, and then invoke the saveChanges() method on the array to persist the changes.\n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\nupdateInsertDelete();\n\nasync function updateInsertDelete() {    \n  const orders = await db.order.getAll({\n    customer: true, \n    deliveryAddress: true, \n    lines: true\n  });\n\n  //will add line to the first order\n  orders[0].lines.push({\n    product: 'secret weapon',\n    amount: 355\n  });\n  \n  //will delete second row\n  orders.splice(1, 1);\n\n  //will insert a new order with lines, deliveryAddress and set customerId\n  orders.push({\n    orderDate: new Date(2022, 0, 11, 9, 24, 47),\n    customer: {\n      id: 1\n    },\n    deliveryAddress: {\n      name: 'George',\n      street: 'Node street 1',\n      postalCode: '7059',\n      postalPlace: 'Jakobsli',\n      countryCode: 'NO'\n    },\n    lines: [\n      { product: 'Magic tent', amount: 349 }\n    ]\n  });\n\n  await orders.saveChanges();\n\n}\n```\n\n__Deleting many rows__\n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ndeleteRows();\n\nasync function deleteRows() {  \n  let orders = await db.order.getAll({\n    where: x =\u003e x.customer.name.eq('George')\n  });\n\n  await orders.delete();\n}\n```\n__Deleting with concurrency__  \nConcurrent operations can lead to conflicts. When you still want to proceed with the deletion regardless of potential interim changes, the 'overwrite' concurrency strategy can be used. This example demonstrates deleting rows even if the \"delivery address\" has been modified in the meantime. You can read more about concurrency strategies in \u003ca href=\"#user-content-updating-rows\"\u003eUpdating rows\u003c/a\u003e.   \n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ndeleteRows();\n\nasync function deleteRows() {\n  let orders = await db.order.getAll({\n    where: x =\u003e x.deliveryAddress.name.eq('George'),\n    customer: true, \n    deliveryAddress: true, \n    lines: true\n  });\n\n  await orders.delete({\n    deliveryAddress: {\n      concurrency: 'overwrite'\n    }\n  });\n}\n```\n__Batch delete__\n\nWhen removing a large number of records based on a certain condition, batch deletion can be efficient.   \n\nHowever, it's worth noting that batch deletes don't follow the cascade delete behavior by default. To achieve cascading in batch deletes, you must explicitly call the deleteCascade method.  \n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ndeleteRows();\n\nasync function deleteRows() {\n  const filter = db.order.deliveryAddress.name.eq('George');\n  await db.order.delete(filter);\n}\n```\n__Batch delete cascade__\n\nWhen deleting records, sometimes associated data in related tables also needs to be removed. This cascade delete helps maintain database integrity.  \n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ndeleteRows();\n\nasync function deleteRows() {\n  const filter = db.order.deliveryAddress.name.eq('George');\n  await db.order.deleteCascade(filter);\n}\n```\n__Batch delete by primary key__\n\nFor efficiency, you can also delete records directly if you know their primary keys.  \n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ndeleteRows();\n\nasync function deleteRows() {\n  db.customer.delete([{id: 1}, {id: 2}]);\n}\n```\n\u003c/details\u003e\n\n\u003cdetails id=\"in-the-browser\"\u003e\u003csummary\u003e\u003cstrong\u003eIn the browser\u003c/strong\u003e\u003c/summary\u003e\n\u003cp\u003eYou can use \u003cstrong\u003e\u003ci\u003eOrange\u003c/i\u003e\u003c/strong\u003e in the browser by using the adapter for Express. Instead of sending raw SQL queries from the client to the server, this approach records the method calls in the client. These method calls are then replayed at the server, ensuring a higher level of security by not exposing raw SQL on the client side.  \nRaw sql queries, raw sql filters and transactions are disabled at the http client due to security reasons.  If you would like Orange to support other web frameworks, like nestJs, fastify, etc, please let me know.\u003c/p\u003e\n\n\u003csub\u003e📄 server.ts\u003c/sub\u003e\n```javascript\nimport map from './map';\nimport { json } from 'body-parser';\nimport express from 'express';\nimport cors from 'cors';\n\nconst db = map.sqlite('demo.db');\n\nexpress().disable('x-powered-by')\n  .use(json({ limit: '100mb' }))\n  .use(cors())\n  //for demonstrational purposes, authentication middleware is not shown here.\n  .use('/orange', db.express())\n  .listen(3000, () =\u003e console.log('Example app listening on port 3000!'));\n```\n\n\u003csub\u003e📄 browser.ts\u003c/sub\u003e\n```javascript\nimport map from './map';\n\nconst db = map.http('http://localhost:3000/orange');\n\nupdateRows();\n\nasync function updateRows() {\n  const order = await db.order.getOne(undefined, {\n    where: x =\u003e x.lines.any(line =\u003e line.product.startsWith('Magic wand'))\n      .and(x.customer.name.startsWith('Harry'),\n    lines: true\n  });\n  \n  order.lines.push({\n    product: 'broomstick',\n    amount: 300,\n  });\n\n  await order.saveChanges();\n}\n\n```\n\n__Interceptors and base filter__\n\nIn the next setup, axios interceptors are employed on the client side to add an Authorization header of requests. Meanwhile, on the server side, an Express middleware (validateToken) is utilized to ensure the presence of the Authorization header, while a base filter is applied on the order table to filter incoming requests based on the customerId extracted from this header. This combined approach enhances security by ensuring that users can only access data relevant to their authorization level and that every request is accompanied by a token. In real-world applications, it's advisable to use a more comprehensive token system and expand error handling to manage a wider range of potential issues.  \nOne notable side effect compared to the previous example, is that only the order table is exposed for interaction, while all other potential tables in the database remain shielded from direct client access (except for related tables). If you want to expose a table without a baseFilter, just set the tableName to an empty object.    \n\n\u003csub\u003e📄 server.ts\u003c/sub\u003e\n\n```javascript\nimport map from './map';\nimport { json } from 'body-parser';\nimport express from 'express';\nimport cors from 'cors';\n\nconst db = map.sqlite('demo.db');\n\nexpress().disable('x-powered-by')\n  .use(json({ limit: '100mb' }))\n  .use(cors())\n  .use('/orange', validateToken)\n  .use('/orange', db.express({\n    order: {\n      baseFilter: (db, req, _res) =\u003e {\n        const customerId = Number.parseInt(req.headers.authorization.split(' ')[1]); //Bearer 2\n        return db.order.customerId.eq(Number.parseInt(customerId));\n      }\n    }\n  }))\n  .listen(3000, () =\u003e console.log('Example app listening on port 3000!'));\n\nfunction validateToken(req, res, next) {\n  // For demo purposes, we're just checking against existence of authorization header\n  // In a real-world scenario, this would be a dangerous approach because it bypasses signature validation\n  const authHeader = req.headers.authorization;\n  if (authHeader)\n    return next();\n  else\n    return res.status(401).json({ error: 'Authorization header missing' });\n}\n```\n\n\u003csub\u003e📄 browser.ts\u003c/sub\u003e\n\n```javascript\nimport map from './map';\n\nconst db = map.http('http://localhost:3000/orange');\n\nupdateRows();\n\nasync function updateRows() {\n  \n  db.interceptors.request.use((config) =\u003e {\n    // For demo purposes, we're just adding hardcoded token\n    // In a real-world scenario, use a proper JSON web token\n    config.headers.Authorization = 'Bearer 2' //customerId\n    return config;\n  });\n\n  db.interceptors.response.use(\n    response =\u003e response, \n    (error) =\u003e {\n      if (error.response \u0026\u0026 error.response.status === 401) {\n        console.dir('Unauthorized, dispatch a login action');\n        //redirectToLogin();\n      }\n      return Promise.reject(error);\n    }\n  );\n\n  const order = await db.order.getOne(undefined, {\n    where: x =\u003e x.lines.any(line =\u003e line.product.startsWith('Magic wand'))\n      .and(db.order.customer.name.startsWith('Harry')),\n    lines: true\n  });\n  \n  order.lines.push({\n    product: 'broomstick',\n    amount: 300\n  });\n\n  await order.saveChanges();\n\n}\n\n```\n\u003c/details\u003e\n\n\u003cdetails id=\"fetching-strategies\"\u003e\u003csummary\u003e\u003cstrong\u003eFetching strategies\u003c/strong\u003e\u003c/summary\u003e\n\u003cp\u003eEfficient data retrieval is crucial for the performance and scalability of applications. The fetching strategy gives you the freedom to decide how much related data you want to pull along with your primary request. Below are examples of common fetching strategies, including fetching entire relations and subsets of columns. When no fetching strategy is present, it will fetch all columns without its relations.\u003cp\u003e\n\n\n__Including a relation__  \nThis example fetches orders and their corresponding delivery addresses, including all columns from both entities.\n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const rows = await db.order.getAll({\n    deliveryAddress: true \n  });  \n}\n```\n\n__Including a subset of columns__  \nIn scenarios where only specific fields are required, you can specify a subset of columns to include. In the example below, orderDate is explicitly excluded, so all other columns in the order table are included by default. For the deliveryAddress relation, only countryCode and name are included, excluding all other columns. If you have a mix of explicitly included and excluded columns, all other columns will be excluded from that table.\n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const rows = await db.order.getAll({\n    orderDate: false,\n    deliveryAddress: {\n      countryCode: true,\n      name: true\n    } \n  });  \n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails id=\"basic-filters\"\u003e\u003csummary\u003e\u003cstrong\u003eBasic filters\u003c/strong\u003e\u003c/summary\u003e\n\u003cp\u003eFilters are a versatile tool for both data retrieval and bulk deletions. They allow for precise targeting of records based on specific criteria and can be combined with operators like \u003ci\u003eany\u003c/i\u003e and \u003ci\u003eexists\u003c/i\u003e and even raw sql for more nuanced control. Filters can also be nested to any depth, enabling complex queries that can efficiently manage and manipulate large datasets. This dual functionality enhances database management by ensuring data relevance and optimizing performance.\u003c/p\u003e\n\n\n__Equal__  \n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const rows = await db.customer.getAll({\n    where x =\u003e x.name.equal('Harry')\n  });\n}\n```\n__Not equal__  \n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const rows = await db.customer.getAll({\n    where x =\u003e x.name.notEqual('Harry')\n  });\n}\n```\n__Contains__  \n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const rows = await db.customer.getAll({\n    where: x =\u003e x.name.contains('arr')\n  });\n}\n```\n__Starts with__  \n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const filter = db.customer.name.startsWith('Harr');\n\n  const rows = await db.customer.getAll({\n    where: x =\u003e x.name.startsWith('Harr')\n  });\n}\n```\n__Ends with__  \n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const rows = await db.customer.getAll({\n    where: x =\u003e x.name.endsWith('arry')\n  });\n}\n```\n__Greater than__  \n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const rows = await db.order.getAll({\n    where: x =\u003e x.orderDate.greaterThan('2023-07-14T12:00:00')\n  });\n}\n```\n__Greater than or equal__  \n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const rows = await db.order.getAll({\n    where: x =\u003e x.orderDate.greaterThanOrEqual('2023-07-14T12:00:00')\n  });\n}\n```\n__Less than__  \n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const rows = await db.order.getAll({\n    where: x =\u003e x.orderDate.lessThan('2023-07-14T12:00:00')\n  });\n}\n```\n__Less than or equal__  \n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const rows = await db.order.getAll({\n    where: x =\u003e x.orderDate.lessThanOrEqual('2023-07-14T12:00:00')\n  });\n}\n```\n__Between__  \n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const rows = await db.order.getAll({\n    where: x =\u003e x.orderDate.between('2023-07-14T12:00:00', '2024-07-14T12:00:00')\n  });\n}\n```\n__In__  \n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const rows = await db.order.getAll({\n    where: x =\u003e x.customer.name.in('George', 'Harry')\n  });\n\n}\n```\n__Raw sql filter__  \nYou can use the raw SQL filter alone or in combination with a regular filter. \nHere the raw filter queries for customer with name ending with \"arry\". The composite filter combines the raw SQL filter and a regular filter that checks for a customer balance greater than 100. It is important to note that due to security precautions aimed at preventing SQL injection attacks, using raw SQL filters directly via browser inputs is not allowed. Attempting to do so will result in an HTTP status 403 (Forbidden) being returned.\n \n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const rawFilter = {\n    sql: 'name like ?',\n    parameters: ['%arry']\n  };                 \n  \n  const rowsWithRaw = await db.customer.getAll({\n    where: () =\u003e rawFilter\n  });\n\n  const rowsWithCombined = await db.customer.getAll({\n    where: x =\u003e x.balance.greaterThan(100).and(rawFilter)\n  });  \n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails id=\"filtering-relations\"\u003e\u003csummary\u003e\u003cstrong\u003eRelation filters\u003c/strong\u003e\u003c/summary\u003e\n\u003cp\u003eRelation filters offer a dynamic approach to selectively include or exclude related data based on specific criteria. In the provided example, all orders are retrieved, yet it filters the order lines to only include those that feature products with \"broomstick\" in their description.  By setting deliveryAddress and customer to true, we also ensure the inclusion of these related entities in our result set.\u003c/p\u003e\n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const orders = await db.order.getAll({\n    lines: {\n      where: x =\u003e x.product.contains('broomstick')\n    },\n    deliveryAddress: true,\n    customer: true\n  });\n}\n```\n\u003c/details\u003e\n\n\u003cdetails id=\"logical-filters\"\u003e\u003csummary\u003e\u003cstrong\u003eAnd, or, not, exists\u003c/strong\u003e\u003c/summary\u003e\n\u003cp\u003eThese operators serve as the backbone for constructing complex queries that allow for more granular control over the data fetched from the database. The examples provided below are self-explanatory for anyone familiar with basic programming concepts and database operations. The design philosophy underscores the importance of clear, readable code that doesn't sacrifice power for simplicity.\u003c/p\u003e\n\n__And__  \n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const rows = await db.order.getAll({\n    where: x =\u003e x.customer.name.equal('Harry')\n      .and(x.orderDate.greaterThan('2023-07-14T12:00:00'))\n  });  \n}\n```\n__Or__  \n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n\n  const rows = await db.order.getAll({\n    where: y =\u003e y.customer( x =\u003e x.name.equal('George')\n      .or(x.name.equal('Harry')))\n  });  \n}\n```\n__Not__  \n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  //Neither George nor Harry\n  const rows = await db.order.getAll({\n    where: y =\u003e y.customer(x =\u003e x.name.equal('George')\n        .or(x.name.equal('Harry')))\n      .not()\n  });  \n}\n```\n__Exists__  \n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const rows = await db.order.getAll({\n    where: x =\u003e x.deliveryAddress.exists()\n  });  \n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails id=\"any-filters\"\u003e\u003csummary\u003e\u003cstrong\u003eAny, all, none\u003c/strong\u003e\u003c/summary\u003e\n\u003cp\u003eThese operators are used in scenarios involving relationships within database records.\u003c/p\u003e\n\n\n__Any__  \nThe \u003ci\u003eany\u003c/i\u003e operator is employed when the objective is to find records where at least one item in a collection meets the specified criteria.\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const rows = await db.order.getAll({\n    where: y =\u003e y.lines.any(x =\u003e x.product.contains('guitar'))\n    //equivalent syntax:\n    //where: x =\u003e x.lines.product.contains('guitar')\n  });  \n}\n```\n__All__  \nConversely, the \u003ci\u003eall\u003c/i\u003e operator ensures that every item in a collection adheres to the defined condition.\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const rows = await db.order.getAll({\n    where: y =\u003e y.lines.all(x =\u003e x.product.contains('a'))\n  });  \n}\n```\n__None__  \nThe \u003ci\u003enone\u003c/i\u003e operator, as the name suggests, is used to select records where not a single item in a collection meets the condition. \n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const rows = await db.order.getAll({\n    where: y =\u003e y.lines.none(x =\u003e x.product.equal('Magic wand'))\n  });  \n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eTransactions\u003c/strong\u003e\u003c/summary\u003e\n\u003cp\u003eWe initiate a database transaction using db.transaction.\nWithin the transaction, a customer is retrieved and its balance updated using the tx object to ensure operations are transactional.\nAn error is deliberately thrown to demonstrate a rollback, ensuring all previous changes within the transaction are reverted.\nAlways use the provided tx object for operations within the transaction to maintain data integrity.\u003c/p\u003e\n\u003cp\u003e(NOTE: Transactions are not supported for Cloudflare D1)\u003c/p\u003e\n\n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\nexecute();\n\nasync function execute() {\n  await db.transaction(async tx =\u003e {\n    const customer = await tx.customer.getById(1);\n      customer.balance = 100;\n      await customer.saveChanges();\n      throw new Error('This will rollback');\n  });\n}\n\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eData types\u003c/strong\u003e\u003c/summary\u003e\n\u003cp\u003eOrange is database agnostic - meaning it can work with multiple database systems without being specifically tied to any one of them. When the ORM behaves consistently across various databases, developers don't need to remember specific quirks or differences when switching between databases. They can rely on the ORM to provide the same mapping behavior, which reduces the cognitive load and potential for errors. There are currently 8 column types in Orange:\u003c/p\u003e\n\n- **`string`** maps to VARCHAR or TEXT in sql\n- **`numeric`** maps to INTEGER, DECIMAL, NUMERIC, TINYINT FLOAT/REAL or DOUBLE in sql.\n- **`bigint`** maps to INTEGER, BIGINT in sql.\n- **`boolean`** maps to BIT, TINYINT(1) or INTEGER in sql.\n- **`uuid`** is represented as string in javascript and maps to UUID, GUID or VARCHAR in sql.\n- **`date`** is represented as ISO 8601 string  in javascript and maps to DATE, DATETIME, TIMESTAMP or DAY in sql. Representing datetime values as ISO 8601 strings, rather than relying on JavaScript's native Date object, has multiple advantages, especially when dealing with databases and servers in different time zones. The datetime values are inherently accompanied by their respective time zones. This ensures that the datetime value remains consistent regardless of where it's being viewed or interpreted. On the other hand, JavaScript's Date object is typically tied to the time zone of the environment in which it's executed, which could lead to inconsistencies between the client and the database server.\n- **`dateWithTimeZone`** is represented as ISO 8601 string  in javascript and maps to TIMESTAMP WITH TIME ZONE in postgres and DATETIMEOFFSET in ms sql.\u003cbr\u003e Contrary to what its name might imply, timestamptz (TIMESTAMP WITH TIME ZONE) in postgres doesn't store the time zone data. Instead, it adjusts the provided time value to UTC (Coordinated Universal Time) before storing it. When a timestamptz value is retrieved, PostgreSQL will automatically adjust the date-time to the time zone setting of the PostgreSQL session (often the server's timezone, unless changed by the user). The primary benefit of DATETIMEOFFSET in ms sql is its ability to keep track of the time zone context. If you're dealing with global applications where understanding the original time zone context is critical (like for coordinating meetings across time zones or logging events), DATETIMEOFFSET is incredibly valuable.\n- **`binary`** is represented as a base64 string in javascript and maps to BLOB, BYTEA or VARBINARY(max) in sql.\n- **`json`** and **`jsonOf\u003cT\u003e`** are represented as an object or array in javascript and maps to JSON, JSONB, NVARCHAR(max) or TEXT (sqlite) in sql.\n\n\u003csub\u003e📄 map.ts\u003c/sub\u003e\n```javascript\nimport orange from 'orange-orm';\n\ninterface Pet {\n    name: string;\n    kind: string;\n}\n\nconst map = orange.map(x =\u003e ({\n    demo: x.table('demo').map(x =\u003e ({\n      id: x.column('id').uuid().primary().notNull(),\n      name: x.column('name').string(),\n      balance: x.column('balance').numeric(),\n      discordId: x.column('balance').bigint(),\n      regularDate: x.column('regularDate').date(),\n      tzDate: x.column('tzDate').dateWithTimeZone(),\n      picture: x.column('picture').binary(),\n      pet: x.column('pet').jsonOf\u003cPet\u003e(), //generic\n      pet2: x.column('pet2').json(), //non-generic\n  }))\n}));\n```\n\u003csub\u003e📄 map.js\u003c/sub\u003e\n```javascript\nimport orange from 'orange-orm';\n\n/**\n * @typedef {Object} Pet\n * @property {string} name - The name of the pet.\n * @property {string} kind - The kind of pet\n */\n\n/** @type {Pet} */\nlet pet;\n\nconst map = orange.map(x =\u003e ({\n    demo: x.table('demo').map(x =\u003e ({\n      id: x.column('id').uuid().primary().notNull(),\n      name: x.column('name').string(),\n      balance: x.column('balance').numeric(),\n      regularDate: x.column('regularDate').date(),\n      tzDate: x.column('tzDate').dateWithTimeZone(),\n      picture: x.column('picture').binary(),\n      pet: x.column('pet').jsonOf(pet), //generic\n      pet2: x.column('pet2').json(), //non-generic\n  }))\n}));\n```\n\u003c/details\u003e\n\n\u003cdetails id=\"default-values\"\u003e\u003csummary\u003e\u003cstrong\u003eDefault values\u003c/strong\u003e\u003c/summary\u003e\n\u003cp\u003eUtilizing default values can be especially useful for automatically populating these fields when the underlying database doesn't offer native support for default value generation.  \n\nIn the provided code, the id column's default value is set to a UUID generated by crypto.randomUUID(), and the isActive column's default is set to true.\u003c/p\u003e\n\n```javascript\nimport orange from 'orange-orm';\nimport crypto 'crypto';\n\nconst map = orange.map(x =\u003e ({\n  myTable: x.table('myTable').map(({ column }) =\u003e ({\n    id: column('id').uuid().primary().default(() =\u003e crypto.randomUUID()),\n    name: column('name').string(),\n    balance: column('balance').numeric(),\n    isActive: column('isActive').boolean().default(true),\n  }))\n}));\n\nexport default map;\n```  \n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eValidation\u003c/strong\u003e\u003c/summary\u003e\n\u003cp\u003eIn the previous sections you have already seen the \u003cstrong\u003e\u003ci\u003enotNull()\u003c/i\u003e\u003c/strong\u003e validator being used on some columns. This will not only generate correct typescript mapping, but also throw an error if value is set to null or undefined. However, sometimes we do not want the notNull-validator to be run on inserts. Typically, when we have an autoincremental key or server generated uuid, it does not make sense to check for null on insert. This is where \u003cstrong\u003e\u003ci\u003enotNullExceptInsert()\u003c/strong\u003e\u003c/i\u003e comes to rescue. You can also create your own custom validator as shown below. The last kind of validator, is the \u003ca href=\"https://ajv.js.org/json-schema.html\"\u003eajv JSON schema validator\u003c/a\u003e. This can be used on json columns as well as any other column type.\u003c/p\u003e\n\n\u003csub\u003e📄 map.ts\u003c/sub\u003e\n```javascript\nimport orange from 'orange-orm';\n\ninterface Pet {\n    name: string;\n    kind: string;\n}\n\nlet petSchema = {\n    \"properties\": {\n        \"name\": { \"type\": \"string\" },\n        \"kind\": { \"type\": \"string\" }\n    }\n};\n\nfunction validateName(value?: string) {\n  if (value \u0026\u0026 value.length \u003e 10)\n    throw new Error('Length cannot exceed 10 characters');\n}\n\nconst map = orange.map(x =\u003e ({\n    demo: x.table('demo').map(x =\u003e ({\n      id: x.column('id').uuid().primary().notNullExceptInsert(),\n      name: x.column('name').string().validate(validateName),\n      pet: x.column('pet').jsonOf\u003cPet\u003e().JSONSchema(petSchema)\n  }))\n}));\n\nexport default map;\n```\n\u003csub\u003e📄 map.js\u003c/sub\u003e\n```javascript\nimport orange from 'orange-orm';\n\n/**\n * @typedef {Object} Pet\n * @property {string} name - The name of the pet.\n * @property {string} kind - The kind of pet\n */\n\n/** @type {Pet} */\nlet pet;\n\nlet petSchema = {\n    \"properties\": {\n        \"name\": { \"type\": \"string\" },\n        \"kind\": { \"type\": \"string\" }\n    }\n};\n\nfunction validateName(value) {\n  if (value \u0026\u0026 value.length \u003e 10)\n    throw new Error('Length cannot exceed 10 characters');\n}\n\nconst map = orange.map(x =\u003e ({\n    demo: x.table('demo').map(x =\u003e ({\n      id: x.column('id').uuid().primary().notNullExceptInsert(),\n      name: x.column('name').string().validate(validateName),\n      pet: x.column('pet').jsonOf(pet).JSONSchema(petSchema)\n  }))\n}));\n\nexport default map;\n```\n\u003c/details\u003e\n\n\u003cdetails id=\"composite-keys\"\u003e\u003csummary\u003e\u003cstrong\u003eComposite keys\u003c/strong\u003e\u003c/summary\u003e\n\u003cp\u003eA composite key is defined by marking multiple columns as primary keys. This is done using the \".primary()\"\" method on each column that is part of the composite key.\n\nConsider a scenario where we have orders and order lines, and each order line is uniquely identified by combining the order type, order number, and line number.\u003c/p\u003e\n\n```javascript\nimport orange from 'orange-orm';\n\nconst map = orange.map(x =\u003e ({\n  order: x.table('_order').map(({ column }) =\u003e ({\n    orderType: column('orderType').string().primary().notNull(),\n    orderNo: column('orderNo').numeric().primary().notNull(),\n    orderDate: column('orderDate').date().notNull(),\n  })),\n\n  orderLine: x.table('orderLine').map(({ column }) =\u003e ({\n    orderType: column('orderType').string().primary().notNull(),\n    orderNo: column('orderNo').numeric().primary().notNull(),\n    lineNo: column('lineNo').numeric().primary().notNull(),\n    product: column('product').string(),\n  }))\n})).map(x =\u003e ({\n  order: x.order.map(v =\u003e ({\n    lines: v.hasMany(x.orderLine).by('orderType', 'orderNo'),\n  }))\n}));\n\nexport default map;\n```  \n\u003c/details\u003e\n\n\n\u003cdetails id=\"column-discriminators\"\u003e\u003csummary\u003e\u003cstrong\u003eColumn discriminators\u003c/strong\u003e\u003c/summary\u003e\n\u003cp\u003eColumn discriminators are used to distinguish between different types of data in the same table. Think of them as labels that identify whether a record is one category or another.\nIn the example, the \u003cstrong\u003eclient_type\u003c/strong\u003e column serves as the discriminator that labels records as \u003cstrong\u003ecustomer\u003c/strong\u003e or \u003cstrong\u003evendor\u003c/strong\u003e in the 'client' table. On inserts, the column will automatically be given the correct discriminator value. Similarly, when fetching and deleting, the discrimiminator will be added to the WHERE clause.\u003c/p\u003e\n\n```javascript\nimport orange from 'orange-orm';\n\nconst map = orange.map(x =\u003e ({\n  customer: x.table('client').map(({ column }) =\u003e ({\n    id: column('id').numeric().primary(),\n    name: column('name').string()\n  })).columnDiscriminators(`client_type='customer'`),\n\n  vendor: x.table('client').map(({ column }) =\u003e ({\n    id: column('id').numeric().primary(),\n    name: column('name').string()\n  })).columnDiscriminators(`client_type='vendor'`),\n}));\n\nexport default map;\n```  \n\u003c/details\u003e\n\n\u003cdetails id=\"formula-discriminators\"\u003e\u003csummary\u003e\u003cstrong\u003eFormula discriminators\u003c/strong\u003e\u003c/summary\u003e\n\u003cp\u003eFormula discriminators are used to distinguish between different types of data in the same table. They differ from column discriminators by using a logical expression rather than a static value in a column.\n\nIn the example below, the formula discriminator categorize bookings into \u003cstrong\u003ecustomerBooking\u003c/strong\u003e and \u003cstrong\u003einternalBooking\u003c/strong\u003e within the same \u003cstrong\u003ebooking\u003c/strong\u003e table. The categorization is based on the value of the \u003cstrong\u003ebooking_no\u003c/strong\u003e column. For \u003cstrong\u003ecustomerBooking\u003c/strong\u003e, records are identified where the booking number falls within the range of 10000 to 99999. For \u003cstrong\u003einternalBooking\u003c/strong\u003e, the range is between 1000 to 9999. These conditions are utilized during fetch and delete operations to ensure that the program interacts with the appropriate subset of records according to their booking number. Unlike column discriminators, formula discriminators are not used during insert operations since they rely on existing data to evaluate the condition.\n\nThe \u003cstrong\u003e\u003ci\u003e'@this'\u003c/strong\u003e\u003c/i\u003e acts as a placeholder within the formula. When Orange constructs a query, it replaces \u003cstrong\u003e'@this'\u003c/strong\u003e with the appropriate alias for the table being queried. This replacement is crucial to avoid ambiguity, especially when dealing with joins with ambigious column names.\u003c/p\u003e\n\n```javascript\nimport orange from 'orange-orm';\n\n\nconst map = orange.map(x =\u003e ({\n  customerBooking: x.table('booking').map(({ column }) =\u003e ({\n    id: column('id').uuid().primary(),\n    bookingNo: column('booking_no').numeric()\n  })).formulaDiscriminators('@this.booking_no between 10000 and 99999'),\n\n  internalBooking: x.table('booking').map(({ column }) =\u003e ({\n    id: column('id').uuid().primary(),\n    bookingNo: column('booking_no').numeric()\n  })).formulaDiscriminators('@this.booking_no between 1000 and 9999'),\n}));\n\nexport default map;\n```  \n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eRaw sql queries\u003c/strong\u003e\u003c/summary\u003e\n\u003cp\u003eYou can employ raw SQL queries directly to fetch rows from the database, bypassing the ORM (Object-Relational Mapper). It is important to note that due to security precautions aimed at preventing SQL injection attacks, using raw SQL filters directly via browser inputs is not allowed. Attempting to do so will result in an HTTP status 403 (Forbidden) being returned.\u003c/p\u003e\n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const query = {\n    sql: 'select * from customer where name like ?',\n    parameters: ['%arry']\n  };\n                 \n  const rows = await db.query(query)   \n}\n```\n\u003c/details\u003e\n\n\u003cdetails id=\"aggregates\"\u003e\u003csummary\u003e\u003cstrong\u003eAggregate functions\u003c/strong\u003e\u003c/summary\u003e\n\nYou can count records and aggregate numerical columns.  This can either be done across rows or separately for each row.  \nSupported functions include:\n- count\n- sum\n- min \n- max  \n- avg  \n\n__On each row__  \nIn this example, we are counting the number of lines on each order.  This is represented as the property \u003ci\u003enumberOfLines\u003c/i\u003e. You can name these aggregated properties whatever you want.  \nYou can also elevate associated data to the a parent level for easier access. In the example below, \u003ci\u003ebalance\u003c/i\u003e of the customer is elevated to the root level.\n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n  const orders = await db.order.getAll({\n    numberOfLines: x =\u003e x.count(x =\u003e x.lines.id),\n    totalAmount: x =\u003e x.sum(x =\u003e lines.amount),\n    balance: x =\u003e x.customer.balance\n  });\n}\n```\n__Across all rows__  \nThe aggregate function effeciently groups data together.\nIn this particular example , for each customer, it counts the number of lines associated with their orders and calculates the total amount of these lines.  \nUnder the hood, it will run an sql group by customerId and customerName.\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetAggregates();\n\nasync function getAggregates() {\n  const orders = await db.order.aggregate({\n    where: x =\u003e x.orderDate.greaterThan(new Date(2022, 0, 11, 9, 24, 47)),\n    customerId: x =\u003e x.customerId,\n    customerName: x =\u003e x.customer.name,\n    numberOfLines: x =\u003e x.count(x =\u003e x.lines.id),\n    totals: x =\u003e x.sum(x =\u003e lines.amount)    \n  });\n}\n```\n\n__Count__  \nFor convenience, you can use the \u003ci\u003ecount\u003c/i\u003e directly on the table instead of using the aggregated query syntax.\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetCount();\n\nasync function getCount() {\n  const filter = db.order.lines.any(\n    line =\u003e line.product.contains('broomstick')\n  );\n  const count = await db.order.count(filter);\n  console.log(count); //2\n}\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eExcluding sensitive data\u003c/strong\u003e\u003c/summary\u003e\n\u003cp\u003eTo secure your application by preventing sensitive data from being serialized and possibly leaked, you can use the \u003cstrong\u003eserializable(false)\u003c/strong\u003e attribute on certain fields within your database schema. Here, the serializable(false) attribute has been applied to the balance column, indicating that this field will not be serialized when a record is converted to a JSON string.\u003c/p\u003e\n\n\u003csub\u003e📄 map.ts\u003c/sub\u003e\n\n```javascript\nimport orange from 'orange-orm';\n\nconst map = orange.map(x =\u003e ({\n  customer: x.table('customer').map(({ column }) =\u003e ({\n    id: column('id').numeric().primary().notNullExceptInsert(),\n    name: column('name').string(),\n    balance: column('balance').numeric().serializable(false),\n    isActive: column('isActive').boolean(),\n  }))\n}));\n\nexport default map;\n```\n\u003csub\u003e📄 sensitive.ts\u003c/sub\u003e\n\n```javascript\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\ngetRows();\n\nasync function getRows() {\n\n  const george = await db.customer.insert({\n    name: 'George',\n    balance: 177,\n    isActive: true\n  });\n  \n  console.dir(JSON.stringify(george), {depth: Infinity});\n  //note that balance is excluded:\n  //'{\"id\":1,\"name\":\"George\",\"isActive\":true}'\n}\n```\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eLogging\u003c/strong\u003e\u003c/summary\u003e\n\u003cp\u003eYou enable logging by listening to the query event on the `orange` object. During this event, both the SQL statement and any associated parameters are logged. The logged output reveals the sequence of SQL commands executed, offering developers a transparent view into database operations, which aids in debugging and ensures data integrity.\u003c/p\u003e\n\n```javascript\nimport orange from 'orange-orm';\nimport map from './map';\nconst db = map.sqlite('demo.db');\n\norange.on('query', (e) =\u003e {\n  console.log(e.sql);\n  if (e.parameters.length \u003e 0)\n    console.log(e.parameters);\n});\n\nupdateRow();\n\nasync function updateRow() {\n  const order = await db.order.getById(2, {\n    lines: true\n  });\n  order.lines.push({\n    product: 'broomstick',\n    amount: 300,\n  });\n\n  await order.saveChanges();\n}\n```\n\noutput:\n```bash\nselect  _order.id as s_order0,_order.orderDate as s_order1,_order.customerId as s_order2 from _order _order where _order.id=2 order by _order.id limit 1\nselect  orderLine.id as sorderLine0,orderLine.orderId as sorderLine1,orderLine.product as sorderLine2,orderLine.amount as sorderLine3 from orderLine orderLine where orderLine.orderId in (2) order by orderLine.id\nBEGIN\nselect  _order.id as s_order0,_order.orderDate as s_order1,_order.customerId as s_order2 from _order _order where _order.id=2 order by _order.id limit 1\nINSERT INTO orderLine (orderId,product,amount) VALUES (2,?,300)\n[ 'broomstick' ]\nSELECT id,orderId,product,amount FROM orderLine WHERE rowid IN (select last_insert_rowid())\nselect  orderLine.id as sorderLine0,orderLine.orderId as sorderLine1,orderLine.product as sorderLine2 from orderLine orderLine where orderLine.orderId in (2) order by orderLine.id\nCOMMIT\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003e\u003cstrong\u003eWhat it is not\u003c/strong\u003e\u003c/summary\u003e\n\u003cp\u003e\n\u003cul\u003e\n  \u003cli\u003e\u003cstrong\u003eIt is not about migrations\u003c/strong\u003e \u003cp\u003eThe allure of ORMs handling SQL migrations is undeniably attractive and sweet. However, this sweetness can become painful. Auto-generated migration scripts might not capture all nuances. Using dedicated migration tools separate from the ORM or manually managing migrations might be the less painful route in the long run.  Orange aim for database agnosticism. And when you're dealing with migrations, you might want to use features specific to a database platform. However, I might consider adding support for (non-auto-generated) migrations at a later point. But for now, it is not on the roadmap.\u003c/p\u003e\u003c/li\u003e\n  \u003cli\u003e\u003cstrong\u003eIt is not about NoSql databases\u003c/strong\u003e \u003cp\u003eApplying ORMs to NoSQL, which inherently diverges from the relational model, can lead to data representation mismatches and a loss of specialized NoSQL features. Moreover, the added ORM layer can introduce performance inefficiencies, complicate debugging, and increase maintenance concerns. Given the unique capabilities of each NoSQL system, crafting custom data access solutions tailored to specific needs often provides better results than a generalized ORM approach.\u003c/p\u003e\u003c/li\u003e\n  \u003cli\u003e\u003cstrong\u003eIt is not about GrapQL\u003c/strong\u003e \u003cp\u003eOrange, already supports remote data operations via HTTP, eliminating the primary need for integrating GraphQL. Orange's built-in safety mechanisms and tailored optimization layers ensure secure and efficient data operations, which might be compromised by adding GraphQL. Furthermore, Orange's inherent expressivity and powerful querying capabilities could be overshadowed by the introduction of GraphQL. Integrating GraphQL could introduce unnecessary complexity, potential performance overhead, and maintenance challenges, especially as both systems continue to evolve. Therefore, considering Orange's robust features and design, supporting GraphQL might not offer sufficient advantages to warrant the associated complications. \u003c/p\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n### [Changelog](https://github.com/alfateam/orange-orm/blob/master/docs/changelog.md)\n### [Code of Conduct](https://github.com/alfateam/orange-orm/blob/master/docs/CODE_OF_CONDUCT.md)\n\u003c!-- \nHow to setup code coverage\nhttps://github.com/nystudio107/rollup-plugin-critical/blob/master/package.json\nhttps://dev.to/thejaredwilcurt/coverage-badge-with-github-actions-finally-59fa --\u003e\n","funding_links":["https://github.com/sponsors/lroal"],"categories":["JavaScript","typescript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falfateam%2Forange-orm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falfateam%2Forange-orm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falfateam%2Forange-orm/lists"}