1.2.1 • Published 5 months ago

rado v1.2.1

Weekly downloads
-
License
MIT
Repository
-
Last release
5 months ago

npm jsr

rado

Fully typed, lightweight TypeScript query builder.

Features

  • Fully typed queries using TypeScript
  • Composable and reusable query structures
  • First-class support for JSON columns
  • No code generation step required
  • Zero dependencies
  • Universal query support for multiple database engines

Installation

Quick Start

import {table} from 'rado'
import {text, integer} from 'rado/postgres'
import {connect} from 'rado/driver/pg'
import {Pool} from 'pg'

// Define your schema
const User = table("user", {
  id: integer().primaryKey(),
  name: text().notNull(),
  email: text().unique()
})

// Connect to the database
const db = connect(new Pool({
  host: 'localhost',
  database: 'my_database'
}))

// Perform a query
const users = await db.select().from(User)
console.log(users)

Supported Databases

Currently supported drivers:

PostgreSQLimport
pgrado/driver/pg
@electric-sql/pgliterado/driver/pglite
@neondatabase/serverlessrado/driver/pg
@vercel/postgresrado/driver/pg
SQLiteimport
better-sqlite3rado/driver/better-sqlite3
bun:sqliterado/driver/bun-sqlite
sql.jsrado/driver/sql.js
@libsql/clientrado/driver/libsql
Cloudflare D1rado/driver/d1
MySQLimport
mysql2rado/driver/mysql2

Pass an instance of the database to the connect function to get started:

import Database from 'better-sqlite3'
import {connect} from 'rado/driver/better-sqlite3'

const db = connect(new Database('foobar.db'))

Querying

Select

Select operations allow you to retrieve data from your database.

// Basic select
const allUsers = await db.select().from(User)

// Select specific columns
const userNames = await db.select({id: User.id, name: User.name}).from(User)

Where conditions

Where conditions help you filter your query results.

import {eq, and, or, gt, isNull} from 'rado'

// Simple equality
const john = await db.select().from(User).where(eq(User.name, 'John'))

// Complex conditions
const users = await db.select().from(User)
  .where(
    gt(User.age, 18),
    or(
      eq(User.name, 'Alice'),
      isNull(User.email)
    )
  )

Joins

Joins allow you to combine data from multiple tables.

const usersWithPosts = await db.select({
  userName: User.name,
  postTitle: Post.title,
})
.from(User)
.leftJoin(Post, eq(User.id, Post.authorId))

Ordering and Grouping

You can order and group your query results:

import {desc, asc, count} from 'rado'

const orderedUsers = await db.select()
  .from(User)
  .orderBy(asc(User.name))

const userPostCounts = await db.select({
  userName: User.name,
  postCount: count(Post.id)
})
.from(User)
.leftJoin(Post, eq(User.id, Post.authorId))
.groupBy(User.name)

Pagination

Pagination helps you manage large datasets by retrieving results in smaller chunks:

const pageSize = 10
const page = 2

const paginatedUsers = await db.select()
  .from(User)
  .limit(pageSize)
  .offset((page - 1) * pageSize)

JSON columns

Rado provides first-class support for JSON columns:

import {pgTable, serial, text, jsonb} from 'rado/postgres'

const User = pgTable("user", {
  id: serial().primaryKey(),
  name: text(),
  metadata: jsonb<{subscribed: boolean}>()
})

const subscribedUsers = await db.select()
  .from(User)
  .where(eq(User.metadata.subscribed, true))

Subqueries

const subquery = db.select({authorId: Post.authorId})
  .from(Post)
  .groupBy(Post.authorId)
  .having(gt(count(Post.id), 5))
  .as('authorIds')

const prolificAuthors = await db.select()
  .from(User)
  .where(inArray(User.id, subquery))

Include

Aggregate rows using the include function:

import {include} from 'rado'

const usersWithPosts = await db.select({
  ...User,
  posts: include(
    db.select().from(Post).where(eq(Post.authorId, User.id))
  )
}).from(User)

// Use include.one for a single related record
const usersWithLatestPost = await db.select({
  ...User,
  latestPost: include.one(
    db.select()
      .from(Post)
      .where(eq(Post.authorId, User.id))
      .orderBy(desc(Post.createdAt))
      .limit(1)
  )
}).from(User)

SQL Operator

The sql operator allows you to write raw SQL and interpolate values safely:

import {sql} from 'rado'

const minAge = 18
const adultUsers = await db.select()
  .from(User)
  .where(sql`${User.age} >= ${minAge}`)

Modifying Data

Insert

Insert operations allow you to add new records to your database:

// Single insert
const newUser = await db.insert(User)
  .values({name: 'Alice', email: 'alice@example.com'})
  .returning()

// Bulk insert
const newUsers = await db.insert(User)
  .values([
    {name: 'Bob', email: 'bob@example.com'},
    {name: 'Charlie', email: 'charlie@example.com'},
  ])
  .returning()

Update

Update operations modify existing records:

const updatedCount = await db.update(User)
  .set({name: 'Johnny'})
  .where(eq(User.name, 'John'))

Delete

Delete operations remove records from your database:

await db.delete(User).where(eq(User.name, 'John'))

Transactions

Transactions allow you to group multiple operations into a single atomic unit.

const result = await db.transaction(async (tx) => {
  const user = await tx.insert(User)
    .values({ name: 'Alice', email: 'alice@example.com' })
    .returning()
  const post = await tx.insert(Post)
    .values({ title: 'My First Post', authorId: user.id })
    .returning()
  return {user, post}
})

Universal Queries

Rado provides a universal query builder that works across different database engines, whether they run synchronously or asynchronously. This is useful for writing database-agnostic code.

import {table} from 'rado'
import {id, text} from 'rado/universal'

const User = table('user', {
  id: id(),
  name: text() 
})

const db = process.env.SQLITE ? sqliteDb : postgresDb

const userNames = await db.select(User.name).from(User)

Custom Column Types

You can define custom column types, such as a boolean column that is stored as a tinyint in the database:

import {Column, column, table, sql} from 'rado'

export function bool(name?: string): Column<boolean | null> {
  return column({
    name,
    type: sql`tinyint(1)`,
    mapFromDriverValue(value: number): boolean {
      return value === 1
    },
    mapToDriverValue(value: boolean): number {
      return value ? 1 : 0
    }
  })
}

// Usage
const User = table('user', {
  // ...
  isActive: bool()
})
1.2.0

6 months ago

1.2.1

5 months ago

1.0.15

7 months ago

1.0.14

9 months ago

1.0.9

12 months ago

1.0.11

11 months ago

1.0.10

11 months ago

1.0.13

10 months ago

1.0.12

11 months ago

1.0.2

1 year ago

1.0.1

1 year ago

1.0.0

1 year ago

1.0.8

12 months ago

1.0.7

12 months ago

1.0.6

12 months ago

1.0.5

12 months ago

1.0.4

12 months ago

1.0.3

12 months ago

1.0.0-preview.4

1 year ago

1.0.0-preview.5

1 year ago

1.0.0-preview.2

1 year ago

1.0.0-preview.3

1 year ago

1.0.0-preview.0

1 year ago

1.0.0-preview.1

1 year ago

0.4.9

1 year ago

0.4.8

2 years ago

0.4.7

2 years ago

0.4.6

2 years ago

0.4.5

2 years ago

0.4.4

2 years ago

0.3.6

2 years ago

0.3.8

2 years ago

0.3.7

2 years ago

0.4.1

2 years ago

0.4.0

2 years ago

0.4.3

2 years ago

0.4.2

2 years ago

0.3.9

2 years ago

0.3.5

2 years ago

0.3.4

2 years ago

0.3.3

2 years ago

0.1.52

3 years ago

0.1.53

3 years ago

0.1.54

3 years ago

0.1.55

3 years ago

0.1.56

3 years ago

0.1.57

3 years ago

0.1.58

3 years ago

0.1.59

3 years ago

0.2.27

2 years ago

0.2.26

2 years ago

0.2.25

2 years ago

0.2.24

2 years ago

0.2.23

2 years ago

0.2.22

2 years ago

0.2.21

2 years ago

0.1.50

3 years ago

0.2.20

3 years ago

0.1.51

3 years ago

0.2.19

3 years ago

0.2.18

3 years ago

0.2.17

3 years ago

0.1.49

3 years ago

0.1.41

3 years ago

0.1.42

3 years ago

0.1.43

3 years ago

0.1.44

3 years ago

0.1.45

3 years ago

0.1.46

3 years ago

0.1.47

3 years ago

0.1.48

3 years ago

0.2.16

3 years ago

0.2.15

3 years ago

0.2.14

3 years ago

0.2.13

3 years ago

0.2.12

3 years ago

0.2.11

3 years ago

0.2.10

3 years ago

0.1.40

3 years ago

0.3.0

2 years ago

0.1.38

3 years ago

0.1.39

3 years ago

0.3.2

2 years ago

0.3.1

2 years ago

0.1.30

3 years ago

0.1.31

3 years ago

0.1.32

3 years ago

0.1.33

3 years ago

0.1.34

3 years ago

0.1.35

3 years ago

0.1.36

3 years ago

0.1.37

3 years ago

0.1.27

3 years ago

0.1.28

3 years ago

0.1.29

3 years ago

0.1.20

3 years ago

0.1.21

3 years ago

0.1.22

3 years ago

0.1.23

3 years ago

0.1.24

3 years ago

0.1.25

3 years ago

0.1.26

3 years ago

0.0.0

3 years ago

0.1.16

3 years ago

0.1.17

3 years ago

0.1.18

3 years ago

0.1.19

3 years ago

0.1.10

3 years ago

0.1.11

3 years ago

0.1.12

3 years ago

0.1.13

3 years ago

0.1.14

3 years ago

0.1.15

3 years ago

0.1.0

3 years ago

0.1.2

3 years ago

0.1.1

3 years ago

0.1.8

3 years ago

0.1.7

3 years ago

0.1.9

3 years ago

0.1.4

3 years ago

0.1.3

3 years ago

0.1.6

3 years ago

0.1.5

3 years ago

0.2.41

2 years ago

0.2.40

2 years ago

0.2.44

2 years ago

0.2.43

2 years ago

0.2.42

2 years ago

0.2.39

2 years ago

0.2.30

2 years ago

0.1.63

3 years ago

0.1.64

3 years ago

0.2.38

2 years ago

0.2.36

2 years ago

0.2.35

2 years ago

0.2.34

2 years ago

0.2.33

2 years ago

0.1.60

3 years ago

0.2.32

2 years ago

0.1.61

3 years ago

0.2.31

2 years ago

0.1.62

3 years ago

0.2.1

3 years ago

0.2.0

3 years ago

0.2.29

2 years ago

0.2.28

2 years ago

0.2.7

3 years ago

0.2.6

3 years ago

0.2.8

3 years ago

0.2.3

3 years ago

0.2.2

3 years ago

0.2.5

3 years ago

0.2.4

3 years ago

0.0.1

10 years ago