{"id":22800775,"url":"https://github.com/fairscript/interact","last_synced_at":"2025-04-19T18:21:36.573Z","repository":{"id":42887678,"uuid":"254692475","full_name":"fairscript/interact","owner":"fairscript","description":"A database interaction library for node.js/JavaScript/TypeScript that uses code reflection to maximize type safety and minimize friction. Supports PostgreSQL, Google BigQuery and SQLite.","archived":false,"fork":false,"pushed_at":"2023-03-04T11:40:55.000Z","size":946,"stargazers_count":6,"open_issues_count":22,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-12-01T11:43:26.034Z","etag":null,"topics":["bigquery","data","database","linq","orm","postgresql","reflection","sql","sqlite","typesafe"],"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/fairscript.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":"Roadmap.md","authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-04-10T17:15:37.000Z","updated_at":"2022-12-07T20:10:28.000Z","dependencies_parsed_at":"2024-09-30T05:40:41.497Z","dependency_job_id":null,"html_url":"https://github.com/fairscript/interact","commit_stats":null,"previous_names":[],"tags_count":46,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fairscript%2Finteract","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fairscript%2Finteract/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fairscript%2Finteract/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fairscript%2Finteract/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fairscript","download_url":"https://codeload.github.com/fairscript/interact/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":229332975,"owners_count":18056651,"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":["bigquery","data","database","linq","orm","postgresql","reflection","sql","sqlite","typesafe"],"created_at":"2024-12-12T08:07:53.321Z","updated_at":"2024-12-12T08:07:53.996Z","avatar_url":"https://github.com/fairscript.png","language":"TypeScript","readme":"# Interact\n\nA database interaction library for node.js/JavaScript/TypeScript that uses code reflection to maximize type safety and minimize friction. Supports PostgreSQL and SQLite.\n\n## Installation\n\nInteract can be installed from npm:\n\n```sh\nnpm install interact\n```\n\nThere is one additional module for each of the three supported databases: \n\n```sh\n# Required for Postgres support\nnpm install interact-with-postgres\n\n# Required for SQLite support\nnpm install interact-with-sqlite\n```\n\n## Getting started\n\n### Step 1: Define a type\n\n```typescript\ninterface Employee {\n    id: number,\n    firstName: string,\n    lastName: string,\n    title: string,\n    salary: number,\n    departmentId: string\n    fulltime: boolean\n}\n```\n\n### Step 2: Define a table\n\n```typescript\nimport { defineTable } from '@fairscript/interact'\n\nconst employees = defineTable\u003cEmployee\u003e(\n    'employees',\n    {\n        id: 'number',\n        firstName: 'string',\n        lastName: 'string',\n        title: 'string',\n        salary: 'integer',\n        departmentId: 'string',\n        fulltime: 'boolean'\n    })\n```\n`defineTable` is a generic function that expects two arguments: the database table name and a record specifying the column types for the specified type.\n\n### Step 3) Create a database context and start running queries\n\n```typescript\nconst dbContext = createSqliteContext(filename)\n\nconst query = employees\n    .filter(e =\u003e e.id === 1)\n    .map(e =\u003e ({ first: e.firstName, last: e.lastName }))\n\nconst namesOfEmployees = dbContext.run(query)\n```\n\nThis generates the following SQL query:\n\n```sql\nSELECT t1.first_name AS first, t1.last_name AS last\nFROM employees t1\nWHERE t1.id = 1\n```\n\n## Table definition\n\n```typescript\nconst employees = defineTable\u003cEmployee\u003e(\n    'employees',\n    {\n        id: 'integer',\n        firstName: 'string',\n        lastName: 'string',\n        title: 'string',\n        salary: 'integer',\n        departmentId: 'integer',\n        fulltime: 'boolean'\n    })\n\nconst departments = defineTable\u003cDepartment\u003e(\n    'departments',\n    {\n        id: 'integer',\n        name: 'string',\n        companyId: 'integer'\n    })\n\nconst companies = defineTable\u003cCompany\u003e(\n    'companies',\n    {\n        id: 'integer',\n        name: 'string'\n    })\n```\n\n## Supported databases\n\n### In-memory SQLite\n\n```typescript\nconst context = createSqliteInMemoryContext()\n```\n\n### On-disk SQLite\n\n```typescript\nconst context = createSqliteOnDiskContext(filename)\n```\n\n### Postgres\n\n```typescript\nimport {Client} from 'pg'\n\nconst pg = new Client(...)\n                      \nawait pg.connect()\n\nconst context = createPostgresContext(pg)\n\nawait pg.end()\n```\n\n## Selection\n\n### Single column\n\n```typescript\nemployees.get(e =\u003e e.id)\n```\n\n### Single row\n\n```typescript\nemployees\n    .filter(e =\u003e e.id === 1)\n    .single()\n```\n\n### Map over rows\n\n```typescript\nemployees\n    .map(e =\u003e ({ firstName: e.firstName, lastName: e.lastName }))\n```\n\n### Single table\n\n```typescript\nemployees.select()\n```\n\n### Limited number of rows\n```typescript\nemployees\n    .select()\n    .limit(n)\n```\n\n### Limited number of rows, with an offset\n```typescript\nemployees\n    .select()\n    .limit(m)\n    .offset(n)\n```\n\n### Distinct rows\n\n```typescript\nemployees\n    .select()\n    .distinct()\n```\n\n## Aggregation\n\n### Number of rows\n\n```typescript\nemployees.count()\n```\n\n### Minimum value in a column\n\n```typescript\nemployees.min(e =\u003e e.salary)\n```\n\n### Maximum value in a column\n\n```typescript\nemployees.max(e =\u003e e.salary)\n```\n\n### Sum of values in a column\n\n```typescript\nemployees.sum(e =\u003e e.salary)\n```\n\n### Average column value\n\n```typescript\nemployees.sum(e =\u003e e.average)\n```\n\n### Selecting multiple aggregations\n\n```typescript\nemployees\n    .aggregate((e, count) =\u003e ({\n        lowestSalary: e.salary.min(),\n        highestSalary: e.salary.max(),\n        totalSalaries: e.salary.sum(),\n        averageSalary: e.salary.average(),\n        numberOfEmployees: count()\n    }))\n```\n\n### Aggregating groups\n\n```typescript\nemployees\n    .groupBy(e =\u003e e.departmentId)\n    .aggregate((key, e, count) =\u003e ({\n        lowestSalary: e.salary.min(),\n        highestSalary: e.salary.max(),\n        totalSalaries: e.salary.sum(),\n        averageSalary: e.salary.average(),\n        employeesInDepartment: count()\n    }))\n```\n\n## Filtering\n\n### Comparison\n\n```typescript\nemployees.filter(e =\u003e e.id == 1)\nemployees.filter(e =\u003e e.id === 1)\n\nemployees.filter(e =\u003e e.id != 1)\nemployees.filter(e =\u003e e.id !== 1)\n\nemployees.filter(e =\u003e e.salary \u003e 10000)\nemployees.filter(e =\u003e e.salary \u003e= 10000)\nemployees.filter(e =\u003e e.salary \u003c 10000)\nemployees.filter(e =\u003e e.salary \u003c= 10000)\n```\n\n### Evaluating a Boolean column\n\n```typescript\nemployees.filter(e =\u003e e.fulltime)\n\nemployees.filter(e =\u003e !e.fulltime)\n```\n\n### Conjunction\n\n```typescript\nemployees.filter(e =\u003e e.firstName === 'John' \u0026\u0026 e.lastName === 'Doe')\n\nemployees\n    .filter(e =\u003e e.firstName === 'John')\n    .filter(e =\u003e e.lastName === 'Doe')\n```\n\n### Disjunction\n\n```typescript\nemployees.filter(e =\u003e e.firstName === 'Jim' \u0026\u0026 e.firstName === 'James')\n```\n\n### Conjunction of disjunctions\n\n```typescript\nemployees.filter(e =\u003e (e.firstName === 'John' || e.firstName === 'Richard') \u0026\u0026 (e.firstName === 'Doe' || e.firstName === 'Roe'))\n```\n\n### Disjunction of conjunctions\n\n```typescript\nemployees.filter(e =\u003e (e.firstName = 'John' \u0026\u0026 e.firstName = 'Doe') || (e.firstName = 'Richard' || e.firstName = 'Roe'))\n```\n\n### User-provided value\n\n```typescript\nemployees.filter(1, (id, e) =\u003e e.id === 1)\n```\n\n### User-provided object\n\n```typescript\nemployees\n    .filter(\n        { firstName: 'John', lastName: 'Doe' },\n        (search, e) =\u003e e.firstName === search.firstName, e.lastName === search.lastName)\n    )\n```\n\n## Sorting features\n\n### Ascending order\n\n```typescript\nemployees\n    .sortBy(e =\u003e e.id)\n    .select()\n```\n\n### Descending order\n\n```typescript\nemployees\n    .sortDescendinglyBy(e =\u003e e.salary)\n    .select()\n```\n\n### Multiple orders\n\n```typescript\nemployees\n    .sortBy(e =\u003e e.departmentId)\n    .thenDescendinglyBy(e =\u003e e.salary)\n    .select()\n```\n\n## Joins\n\n### Joining tables\n\n```javascript\nemployees\n    .join(departments, e =\u003e e.departmentId, d =\u003e d.id)\n    .join(departments, e =\u003e e.companyId, c =\u003e c.id)\n```\n\n### Column from a joined table\n\n```typescript\nemployees\n    .join(departments, e =\u003e e.departmentId, d =\u003e d.id)\n\t.get((e, d) =\u003e d.name)\n```\n\n### Map over rows\n\n```typescript\nemployees\n    .join(departments, e =\u003e e.departmentId, d =\u003e d.id)\n    .get((e, d) =\u003e {\n        firstName: e.firstName,\n    \tlastName: e.lastName,\n        department: d.name\n    })\n```\n\n### Selecting multiple tables\n\n```typescript\nemployees\n    .join(departments, e =\u003e e.departmentId, d =\u003e d.id)\n    .join(companies, d =\u003e d.companyId, c =\u003e c.id)\n    .select('employee', 'department', 'company')\n```\n\n## Subqueries\n\n### Number of rows\n\n```typescript\nemployees.map(\n     employees,\n     (subtable, e) =\u003e ({\n         id: e.id,\n         departmentSize: subtable\n             .filter(se =\u003e se.departmentId === e.departmentId)\n             .count()\n     }))\n```\n\n### Minimum value in a column\n\n```typescript\nemployees.map(\n     employees,\n     (subtable, e) =\u003e ({\n         id: e.id,\n         lowestSalaryInDepartment: subtable\n             .filter(se =\u003e se.departmentId === e.departmentId)\n             .min(se =\u003e se.salary)\n     }))\n```\n\n### Maximum value in a column\n\n```typescript\nemployees.map(\n     employees,\n     (subtable, e) =\u003e ({\n         id: e.id,\n         highestSalaryInDepartment: subtable\n             .filter(se =\u003e se.departmentId === e.departmentId)\n             .max(se =\u003e se.salary)\n     }))\n```\n\n### Sum of values in a column\n\n```typescript\nemployees.map(\n     employees,\n     (subtable, e) =\u003e ({\n         id: e.id,\n         totalSalariesInDepartment: subtable\n             .filter(se =\u003e se.departmentId === e.departmentId)\n             .sum(se =\u003e se.salary)\n     }))\n```\n\n### Average column value\n\n```typescript\nemployees.map(\n     employees,\n     (subtable, e) =\u003e ({\n         id: e.id,\n         averageSalaryInDepartment: subtable\n             .filter(se =\u003e se.departmentId === e.departmentId)\n             .average(se =\u003e se.salary)\n     }))\n```\n\n## Parallel queries\n\n```typescript\nconst promiseOfResults: Promise = context\n\t.parallelRun({\n        numberOfEmployees: employees.count(),\n        numberOfDepartments: departments.count(),\n        numberOfCompanies: companies.count()\n\t})\n    .then(res =\u003e {\n        { numberOfEmployees, numberOfDepartments, numberOfCompanies } = res\n        [...]\n    })\n```","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffairscript%2Finteract","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffairscript%2Finteract","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffairscript%2Finteract/lists"}