{"id":15408104,"url":"https://github.com/benmerckx/rado","last_synced_at":"2025-04-18T15:15:32.867Z","repository":{"id":70464050,"uuid":"580761948","full_name":"benmerckx/rado","owner":"benmerckx","description":"🍃 Fully typed, lightweight query builder","archived":false,"fork":false,"pushed_at":"2024-04-08T04:06:22.000Z","size":1759,"stargazers_count":4,"open_issues_count":2,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2024-04-08T13:25:59.955Z","etag":null,"topics":["bun","node","orm","query-builder","sql","sqlite","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/benmerckx.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2022-12-21T11:37:05.000Z","updated_at":"2024-04-15T07:58:26.750Z","dependencies_parsed_at":"2024-03-18T11:51:34.431Z","dependency_job_id":"444d4d9c-26be-4254-b0a2-75d809bdf8ee","html_url":"https://github.com/benmerckx/rado","commit_stats":null,"previous_names":[],"tags_count":131,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benmerckx%2Frado","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benmerckx%2Frado/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benmerckx%2Frado/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benmerckx%2Frado/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/benmerckx","download_url":"https://codeload.github.com/benmerckx/rado/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249509171,"owners_count":21283542,"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":["bun","node","orm","query-builder","sql","sqlite","typescript"],"created_at":"2024-10-01T16:31:29.043Z","updated_at":"2025-04-18T15:15:32.856Z","avatar_url":"https://github.com/benmerckx.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![npm](https://img.shields.io/npm/v/rado.svg)](https://npmjs.org/package/rado)\n[![jsr](https://jsr.io/badges/@rado/rado)](https://jsr.io/@rado/rado)\n\n# rado\n\nFully typed, lightweight TypeScript query builder.\n\n## Features\n\n- Fully typed queries using TypeScript\n- Composable and reusable query structures\n- First-class support for JSON columns\n- No code generation step required\n- Zero dependencies\n- Universal query support for multiple database engines\n\n## Installation\n\n\u003cpre\u003enpm install \u003ca href=\"https://www.npmjs.com/package/rado\"\u003erado\u003c/a\u003e\u003c/pre\u003e\n\n## Quick Start\n\n```typescript\nimport {table} from 'rado'\nimport {text, integer} from 'rado/postgres'\nimport {connect} from 'rado/driver/pg'\nimport {Pool} from 'pg'\n\n// Define your schema\nconst User = table(\"user\", {\n  id: integer().primaryKey(),\n  name: text().notNull(),\n  email: text().unique()\n})\n\n// Connect to the database\nconst db = connect(new Pool({\n  host: 'localhost',\n  database: 'my_database'\n}))\n\n// Perform a query\nconst users = await db.select().from(User)\nconsole.log(users)\n```\n\n## Supported Databases\n\nCurrently supported drivers:\n\n| `PostgreSQL              ` | `import                      ` |\n| -------------------------- | ------------------------------ |\n| pg                         | rado/driver/pg                 |\n| @electric-sql/pglite       | rado/driver/pglite             |\n| @neondatabase/serverless   | rado/driver/pg                 |\n| @vercel/postgres           | rado/driver/pg                 |\n\n| `SQLite                  ` | `import                      ` |\n| -------------------------- | ------------------------------ |\n| better-sqlite3             | rado/driver/better-sqlite3     |\n| bun:sqlite                 | rado/driver/bun-sqlite         |\n| sql.js                     | rado/driver/sql.js             |\n| @libsql/client             | rado/driver/libsql             |\n| Cloudflare D1              | rado/driver/d1                 |\n\n| `MySQL                   ` | `import                      ` |\n| -------------------------- | ------------------------------ |\n| mysql2                     | rado/driver/mysql2             |\n\nPass an instance of the database to the `connect` function to get started:\n\n```ts\nimport Database from 'better-sqlite3'\nimport {connect} from 'rado/driver/better-sqlite3'\n\nconst db = connect(new Database('foobar.db'))\n```\n\n## Querying\n\n### Select\n\nSelect operations allow you to retrieve data from your database.\n\n```typescript\n// Basic select\nconst allUsers = await db.select().from(User)\n\n// Select specific columns\nconst userNames = await db.select({id: User.id, name: User.name}).from(User)\n```\n\n### Where conditions\n\nWhere conditions help you filter your query results.\n\n```typescript\nimport {eq, and, or, gt, isNull} from 'rado'\n\n// Simple equality\nconst john = await db.select().from(User).where(eq(User.name, 'John'))\n\n// Complex conditions\nconst users = await db.select().from(User)\n  .where(\n    gt(User.age, 18),\n    or(\n      eq(User.name, 'Alice'),\n      isNull(User.email)\n    )\n  )\n```\n\n### Joins\n\nJoins allow you to combine data from multiple tables.\n\n```typescript\nconst usersWithPosts = await db.select({\n  userName: User.name,\n  postTitle: Post.title,\n})\n.from(User)\n.leftJoin(Post, eq(User.id, Post.authorId))\n```\n\n### Ordering and Grouping\n\nYou can order and group your query results:\n\n```typescript\nimport {desc, asc, count} from 'rado'\n\nconst orderedUsers = await db.select()\n  .from(User)\n  .orderBy(asc(User.name))\n\nconst userPostCounts = await db.select({\n  userName: User.name,\n  postCount: count(Post.id)\n})\n.from(User)\n.leftJoin(Post, eq(User.id, Post.authorId))\n.groupBy(User.name)\n```\n\n### Pagination\n\nPagination helps you manage large datasets by retrieving results in smaller chunks:\n\n```typescript\nconst pageSize = 10\nconst page = 2\n\nconst paginatedUsers = await db.select()\n  .from(User)\n  .limit(pageSize)\n  .offset((page - 1) * pageSize)\n```\n\n### JSON columns\n\nRado provides first-class support for JSON columns:\n\n```typescript\nimport {pgTable, serial, text, jsonb} from 'rado/postgres'\n\nconst User = pgTable(\"user\", {\n  id: serial().primaryKey(),\n  name: text(),\n  metadata: jsonb\u003c{subscribed: boolean}\u003e()\n})\n\nconst subscribedUsers = await db.select()\n  .from(User)\n  .where(eq(User.metadata.subscribed, true))\n```\n\n### Subqueries\n\n```typescript\nconst subquery = db.select({authorId: Post.authorId})\n  .from(Post)\n  .groupBy(Post.authorId)\n  .having(gt(count(Post.id), 5))\n  .as('authorIds')\n\nconst prolificAuthors = await db.select()\n  .from(User)\n  .where(inArray(User.id, subquery))\n```\n\n### Include\n\nAggregate rows using the `include` function:\n\n```typescript\nimport {include} from 'rado'\n\nconst usersWithPosts = await db.select({\n  ...User,\n  posts: include(\n    db.select().from(Post).where(eq(Post.authorId, User.id))\n  )\n}).from(User)\n\n// Use include.one for a single related record\nconst usersWithLatestPost = await db.select({\n  ...User,\n  latestPost: include.one(\n    db.select()\n      .from(Post)\n      .where(eq(Post.authorId, User.id))\n      .orderBy(desc(Post.createdAt))\n      .limit(1)\n  )\n}).from(User)\n```\n\n### SQL Operator\n\nThe `sql` operator allows you to write raw SQL and interpolate values safely:\n\n```typescript\nimport {sql} from 'rado'\n\nconst minAge = 18\nconst adultUsers = await db.select()\n  .from(User)\n  .where(sql`${User.age} \u003e= ${minAge}`)\n```\n\n## Modifying Data\n\n### Insert\n\nInsert operations allow you to add new records to your database:\n\n```typescript\n// Single insert\nconst newUser = await db.insert(User)\n  .values({name: 'Alice', email: 'alice@example.com'})\n  .returning()\n\n// Bulk insert\nconst newUsers = await db.insert(User)\n  .values([\n    {name: 'Bob', email: 'bob@example.com'},\n    {name: 'Charlie', email: 'charlie@example.com'},\n  ])\n  .returning()\n```\n\n### Update\n\nUpdate operations modify existing records:\n\n```typescript\nconst updatedCount = await db.update(User)\n  .set({name: 'Johnny'})\n  .where(eq(User.name, 'John'))\n```\n\n### Delete\n\nDelete operations remove records from your database:\n\n```typescript\nawait db.delete(User).where(eq(User.name, 'John'))\n```\n\n### Transactions\n\nTransactions allow you to group multiple operations into a single atomic unit.\n\n```typescript\nconst result = await db.transaction(async (tx) =\u003e {\n  const user = await tx.insert(User)\n    .values({ name: 'Alice', email: 'alice@example.com' })\n    .returning()\n  const post = await tx.insert(Post)\n    .values({ title: 'My First Post', authorId: user.id })\n    .returning()\n  return {user, post}\n})\n```\n\n## Universal Queries\n\nRado provides a universal query builder that works across different database \nengines, whether they run synchronously or asynchronously. This is useful for\nwriting database-agnostic code.\n\n```typescript\nimport {table} from 'rado'\nimport {id, text} from 'rado/universal'\n\nconst User = table('user', {\n  id: id(),\n  name: text() \n})\n\nconst db = process.env.SQLITE ? sqliteDb : postgresDb\n\nconst userNames = await db.select(User.name).from(User)\n```\n\n## Custom Column Types\n\nYou can define custom column types, such as a boolean column that is stored as a\ntinyint in the database:\n\n```typescript\nimport {Column, column, table, sql} from 'rado'\n\nexport function bool(name?: string): Column\u003cboolean | null\u003e {\n  return column({\n    name,\n    type: sql`tinyint(1)`,\n    mapFromDriverValue(value: number): boolean {\n      return value === 1\n    },\n    mapToDriverValue(value: boolean): number {\n      return value ? 1 : 0\n    }\n  })\n}\n\n// Usage\nconst User = table('user', {\n  // ...\n  isActive: bool()\n})\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenmerckx%2Frado","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbenmerckx%2Frado","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenmerckx%2Frado/lists"}