Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:\n\n```ts\ntype Fragment = { sql:string, params:Record\u003cstring,any\u003e }\n```\n\n\u003clive-preview\u003e\nJSON.stringify( litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


id = ${1} OR name = ${'John'}`)\n\u003c/live-preview\u003e\n\n### SQL Builder\n\nSQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:\n\n```ts\ninterface SqlBuilder {\n build(): Fragment\n}\n```\n\nTheir simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.\n\n### litdb drivers\n\nThe litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments \nfor their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst\nremote databases like PostgreSQL and MySQL only support the Async DbConnection.\n\n```ts\ninterface SyncDbConnection {\n driver:Driver\n $:Function\n schema:Schema\n quote(symbol:string): string\n insert\u003cT extends ClassInstance\u003e(row:T, options?:InsertOptions): Changes\n insertAll\u003cT extends ClassInstance\u003e(rows:T[], options?:InsertOptions): Changes\n update\u003cT extends ClassInstance\u003e(row:T, options?:UpdateOptions): Changes\n delete\u003cT extends ClassInstance\u003e(row:T, options?:DeleteOptions): Changes\n listTables(): string[]\n dropTable\u003cTable extends ClassParam\u003e(table:Table): void\n createTable\u003cTable extends ClassParam\u003e(table:Table): void\n all\u003cRetType\u003e(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment\u003cRetType\u003e, ...params: any[]): RetType[]\n one\u003cRetType\u003e(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment\u003cRetType\u003e, ...params: any[]): RetType\n column\u003cReturnValue\u003e(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]\n value\u003cReturnValue\u003e(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue\n arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]\n array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]\n exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes\n run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void\n prepareSync\u003cT\u003e(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement \n close()\n}\n\ntype Changes = { changes: number; lastInsertRowid: number | bigint }\n```\n\n## Data Models\n\nlitdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with \n[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.\n\n## Safe by default\n\nAll SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes \nall values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:\n\n\u003clive-preview\u003e\nconst bobbyTables = \"Robert'); DROP TABLE Students;--\"\ndb.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)\n\u003c/live-preview\u003e\n\nDriver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:\n\n\u003clive-preview\u003e\nconst bobbyTables = \"Robert'); DROP TABLE Students;--\"\ndb.one($.from(Contact).where(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.name} = ${bobbyTables}`))\ndb.one($.from(Contact).where`name = ${bobbyTables}`)\ndb.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`\ndb.one( litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


SELECT * FROM Contact WHERE name = ${bobbyTables}`)\ndb.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))\ndb.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })\n\u003c/live-preview\u003e\n\n## Portable\n\nlitdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL\nsyntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing\nyour App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your \nApp to easily migrate to run on different databases.\n\n## Expressive\n\nAt the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of\na more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating\ntype-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of \nyour RDBMS SQL dialect to make use of any RDBMS-specific features.\n\n\n# Install\nSource: https://razor-press.web-templates.io/install\n\nTo use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:\n\n:::sh\nnpm install litdb\n:::\n\n`litdb` is also available as a module, where it can be used directly in the browser:\n\n```html\n\u003cscript type=\"module\"\u003e\nimport { sqlite as $ } from \"https://unpkg.com/litdb/dist/index.min.js\"\nconst { sql, params } = $.from(Contact).select(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.name}`).build()\n\u003c/script\u003e\n```\n\nTo get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)\n\n# LitDB Drivers\n\nLightweight drivers with first-class support for litdb query builders are also available for the popular databases below:\n\n### SQLite (Bun)\n\nUse with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):\n\n:::sh\nbun install @litdb/bun-sqlite\n:::\n\nSee [litdb Bun SQLite Docs](/bun-sqlite).\n\n### SQLite (Node.js)\n\nUse with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):\n\n:::sh\nnpm install @litdb/better-sqlite\n:::\n\nSee [litdb better-sqlite3 Docs](/better-sqlite).\n\n### PostgreSQL\n\nUse with the [postgres.js](https://github.com/porsager/postgres) client:\n\n:::sh\nnpm install @litdb/postgres\n:::\n\nSee [litdb postgres Docs](/postgres).\n\n### MySQL\n\nUse with the [mysql2](https://github.com/sidorares/node-mysql2) client:\n\n:::sh\nnpm install @litdb/mysql2\n:::\n\nSee [litdb mysql2 Docs](/mysql2).\n\n### Request a Driver\n\nIf you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's \n[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).\n\n## Driver Usage\n\nlitdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. \nThey can be used with or without litdb SQL Builders, but offer the most value when used together. \n\nThe same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs\nrecommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for \nquerying all other remote databases, e.g. PostgreSQL and MySQL.\n\n\n# Models\nSource: https://razor-press.web-templates.io/models\n\nModel definitions are a representation of your RDBMS tables and columns in your App's code that's used to\nconfigure how your App's classes and properties map to your database tables and columns.\n\nThey're used for creating table schemas and is able to influence the SQL that's generated by query builders \nand how results are mapped between your data models and RDBMS.\n\nThey can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively \nannotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. \n\n## Fluent API\n\n```ts\nimport { Table } from 'litdb'\n\nexport class Contact {\n constructor(data) { Object.assign(this, data) }\n id = 0\n name = ''\n age = 0 || undefined\n email = ''\n city = '' || undefined\n createdAt = new Date(2025,1,1)\n}\nexport class Order {\n constructor(data) { Object.assign(this, data) }\n id = 0\n contactId = 0\n total = 0.0\n createdAt = new Date()\n}\nexport class OrderItem {\n constructor(data) { Object.assign(this, data) }\n id = 0\n orderId = 0\n sku = ''\n qty = 0\n total = 0.0\n}\nexport class Product {\n constructor(data) { Object.assign(this, data) }\n sku = ''\n name = ''\n cost = 0.0\n}\n\nTable(Contact, {\n columns: {\n id: { type:\"INTEGER\", autoIncrement:true },\n name: { type:\"TEXT\", required:true },\n age: { type:\"INTEGER\" },\n email: { type:\"TEXT\", required:true, index:true, unique:true },\n city: { type:\"TEXT\" },\n createdAt: { type:\"DATETIME\", defaultValue:\"CURRENT_TIMESTAMP\" },\n }\n})\nTable(Order, {\n columns: {\n id: { type:\"INTEGER\", autoIncrement:true },\n contactId: { type:\"INTEGER\", required:true, references:{ table:Contact, on:[\"DELETE\",\"CASCADE\"] } },\n total: { type:\"MONEY\", required:true },\n createdAt: { type:\"DATETIME\", defaultValue:\"CURRENT_TIMESTAMP\" },\n }\n})\nTable(OrderItem, {\n columns: {\n id: { type:\"INTEGER\", autoIncrement:true },\n orderId: { type:\"INTEGER\", required:true, references:{ table:Order, on:[\"DELETE\",\"RESTRICT\"] } },\n sku: { type:\"TEXT\", required:true, references:{ table:Product, on:[\"DELETE\",\"RESTRICT\"] } },\n qty: { type:\"INTEGER\", required:true },\n total: { type:\"MONEY\", required:true }\n }\n})\nTable(Product, {\n columns: {\n sku: { type:\"TEXT\", primaryKey:true },\n name: { type:\"TEXT\", required:true, index:true, unique:true },\n cost: { type:\"MONEY\", required:true },\n }\n})\n```\n\n## Declarative Annotations\n\nTypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the \n`@table` and `@column` decorators to define their data models, e.g: \n\n```ts\nimport { table, column, DefaultValues } from 'litdb'\n\n@table()\nexport class Contact {\n constructor(data?: Partial\u003cContact\u003e) { Object.assign(this, data) }\n\n @column(\"INTEGER\", { autoIncrement: true })\n id = 0\n \n @column(\"TEXT\", { required: true })\n name = ''\n \n @column(\"INTEGER\")\n age?: number\n\n @column(\"TEXT\", { required:true, index:true, unique:true })\n email = ''\n\n @column(\"TEXT\")\n city = ''\n \n @column(\"DATETIME\", { defaultValue:'CURRENT_TIMESTAMP' })\n createdAt = new Date()\n}\n\n@table()\nexport class Order {\n constructor(data?: Partial\u003cOrder\u003e) { Object.assign(this, data) }\n\n @column(\"INTEGER\", { autoIncrement:true })\n id: number = 0\n\n @column(\"INTEGER\", { required:true, references:{ table:Contact, on:[\"DELETE\",\"CASCADE\"] } })\n contactId: number = 0\n\n @column(\"MONEY\", { required:true})\n total: number = 0\n\n @column(\"DATETIME\", { defaultValue:DefaultValues.NOW })\n createdAt = new Date()\n}\n\n@table()\nexport class OrderItem {\n @column(\"INTEGER\", { autoIncrement:true })\n id: number = 0\n\n @column(\"INTEGER\", { required:true, references:{ table:Order, on:[\"DELETE\",\"RESTRICT\"] } })\n orderId: number = 0\n\n @column(\"TEXT\", { required:true, references:{ table:Product, on:[\"DELETE\",\"RESTRICT\"] } })\n sku: string = ''\n\n @column(\"INTEGER\", { required:true })\n qty: number = 0\n\n @column(\"MONEY\", { required:true })\n total: number = 0\n}\n\n@table()\nexport class Product {\n @column(\"TEXT\", { primaryKey:true })\n sku = ''\n @column(\"TEXT\", { required:true, index:true, unique:true })\n name = ''\n @column(\"MONEY\", { required:true })\n cost = 0.0\n}\n```\n\n### Custom Data Types\n\nWhen needed, a `Symbol` can be used to define custom data types, e.g:\n\n```ts\nclass Address {\n @column(Symbol(\"POINT\"))\n location\n}\n```\n\n\n# Customize\nSource: https://razor-press.web-templates.io/customize\n\n## Custom Naming Strategy\n\nApp's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. \n\n```ts\nimport { SnakeCaseStrategy } from \"litdb\"\nimport { connect } from \"@litdb/postgres\"\n\nexport const connection = connect({hostname, database, user, password})\nexport const { $, async: db, native:sql } = connection\nconnection.dialect.strategy = new SnakeCaseStrategy()\n```\n\nWhere SnakeCaseStrategy is defined as returning table and column names in snake_case:\n\n```ts\nclass SnakeCaseStrategy {\n tableName(table:string) : string { return snakeCase(table) }\n columnName(column:string) : string { return snakeCase(column) }\n}\n```\n\n## Type Converters\n\nlitdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use \ncustom type converters by registering them with the driver's schema.\n\nA Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.\n\n```ts\ninterface TypeConverter {\n toDb(value: any): any;\n fromDb(value: any): any;\n}\n```\n\nFor example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:\n\n```ts\nclass DateTimeConverter implements TypeConverter\n{\n toDb(value: any) {\n const d = toDate(value)\n return d ? dateISOString(d).replace('T',' ') : null\n }\n fromDb(value: any) {\n if (!value) return null\n return toDate(value)\n }\n}\n```\n\nCustom Type Converters can be registered with the driver's schema for the data type it should apply to:\n\n```ts\nexport const connection = connect({ host, database, user, password })\nexport const { $, async:db, native } = connection\n\nconnection.schema.converters['DATETIME'] = new DateTimeConverter()\n```\n\n## Register Converter for Mutliple Data Types\n\nThe `converterFor` utility function can be used to register a converter for multiple data types:\n\n```ts\nimport { converterFor } from \"litdb\"\n\nObject.assign(connection.schema.converters, \n converterFor(new DateConverter(), \"DATE\", \"DATETIME\", \"TIMESTAMP\", \"TIMESTAMPZ\"))\n```\n\n\n# Creating a good Bug Report\nSource: https://razor-press.web-templates.io/bug-report\n\n### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)\n\n\u003e Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project \nor otherwise clear steps to reproduce the issue. For details please see \n[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).\n\n\nThe more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how\nwell it's reported.\n\n## 1) Reproducible:\n\nIf your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.\nDo not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.\nE.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains\nall the assumptions and environment settings necessary for the error to occur.\n\n## 2) Be Specific:\n\nDo not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum\nwords yet in effective way. Do not combine multiple problems even they seem to be similar.\nWrite different reports for each problem.\n\n## 3) Environment Details:\n\nEnsure you're using the latest litdb packages, include the Operating System and the versions of the relevant\nmajor components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.\n\n\n# litdb for Bun SQLite\nSource: https://razor-press.web-templates.io/bun-sqlite\n\nUse litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):\n\n:::sh\nbun install @litdb/bun-sqlite\n:::\n\n## Configuration\n\n**db.ts**\n\n```ts\nimport { connect } from \"@litdb/bun-sqlite\"\n\nexport const connection = connect(\"app.db\") // WAL enabled by default\nexport const { $, sync:db, async, native } = connection\n```\n\n:::tip\nWhen needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) \n:::\n\n### Configuration Options\n\n```ts\ntype ConnectionOptions = {\n // Creates a new database connection to the specified SQLite DB. \n // If the database file does not exist, it is created.\n // default \"app.db\"\n fileName?:string\n // Whether to enable WAL\n // default true\n wal?:boolean\n // Open the database as read-only (no write operations, no create).\n readonly?: boolean\n // Allow creating a new database\n create?: boolean;\n // Open the database as read-write\n readwrite?: boolean;\n // When set to `true`, integers are returned as `bigint` types.\n // When set to `false`, integers are returned as `number` types and truncated to 52 bits.\n // default false\n safeIntegers?: boolean;\n // When set to `false` or `undefined`:\n // - Queries missing bound parameters will NOT throw an error\n // - Bound named parameters in JavaScript need to exactly match the SQL query.\n // default true\n strict?: boolean;\n}\n```\n\nExample:\n\n```ts\nexport const connection = connect({ fileName:'app.db' })\n```\n\n## Usage\n\nExample of using `@litdb/bun-sqlite` sync APIs:\n\n```ts\nimport { $, db } from \"./db\"\nimport { Contact } from \"./models\"\n\ndb.dropTable(Contact)\ndb.createTable(Contact)\ndb.insertAll([\n new Contact({ name:\"John Doe\", email:\"john@mail.org\" }),\n new Contact({ name:\"Jane Doe\", email:\"jane@mail.org\" }),\n])\n\nconst janeEmail = 'jane@mail.org'\nconst jane = db.one\u003cContact\u003e($.from(Contact).where(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.email} = ${janeEmail}`))!\n\n// Insert examples\nconst { lastInsertRowid: bobId } = db.insert(new Contact({ name:\"Bob\", email:\"bob@mail.org\" }))\nconst { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`\nconst name = 'Alice', email = 'alice@mail.org'\ndb.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`\n\n// Typed SQL fragment with named param example\nconst hasId = \u003cTable extends { id:number }\u003e(id:number|bigint) =\u003e\n (x:Table) =\u003e $.sql( litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${x.id} = $id`, { id })\n\nconst contacts = db.all($.from(Contact).into(Contact)) // =\u003e Contact[]\nconst bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // =\u003e Contact \nconst contactsCount = db.value($.from(Contact).select`COUNT(*)`) // =\u003e number\nconst emails = db.column($.from(Contact).select(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.email}`)) // =\u003e string[]\nconst contactsArray = db.arrays($.from(Contact)) // =\u003e any[][]\nconst bobArray = db.array($.from(Contact).where(hasId(bobId))) // =\u003e any[]\n\n// Update examples\njane.email = 'jane@doe.org'\ndb.update(jane) // Update all properties\ndb.update(jane, { onlyProps:['email'] }) // Update only email\ndb.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder\n\n// Delete examples\ndb.delete(jane)\ndb.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder\n```\n\n\n# litdb for Node.js better-sqlite3\nSource: https://razor-press.web-templates.io/better-sqlite\n\nUse litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):\n\n:::sh\nnpm install @litdb/better-sqlite\n:::\n\n## Configuration\n\n**db.ts**\n\n```ts\nimport { connect } from \"@litdb/better-sqlite\"\n\nexport const connection = connect(\"app.db\") // WAL enabled by default\nexport const { $, sync:db, async, native } = connection\n```\n\n:::tip\nWhen needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)\n:::\n\n### Configuration Options\n\n```ts\ntype ConnectionOptions = {\n // Creates a new database connection to the specified SQLite DB. \n // If the database file does not exist, it is created.\n // default: app.db\n fileName?:string\n // Whether to enable WAL\n // default: true\n wal?:boolean\n // Open the database as read-only (no write operations, no create).\n // default: false\n readonly?: boolean\n // If the database does not exist, an Error will be thrown instead of creating a new file\n fileMustExist?: boolean | undefined;\n // The number of milliseconds to wait when executing queries on a locked database, \n // before throwing a SQLITE_BUSY error\n // default: 5000\n timeout?: number | undefined;\n // Provide a function that gets called with every SQL string executed by the db connection\n verbose?: ((message?: unknown, ...additionalArgs: unknown[]) =\u003e void) | undefined;\n // If you're using a build system that moves, transforms, or concatenates your JS files,\n // you can solve it by using this option to provide the file path of better_sqlite3.node \n // (relative to the current working directory).\n nativeBinding?: string | undefined;\n}\n```\n\nExample:\n\n```ts\nexport const connection = connect({ fileName:'app.db' })\n```\n\n## Usage\n\nExample of using `@litdb/better-sqlite` sync APIs:\n\n```ts\nimport { $, db } from \"./db\"\nimport { Contact } from \"./models\"\n\ndb.dropTable(Contact)\ndb.createTable(Contact)\ndb.insertAll([\n new Contact({ name:\"John Doe\", email:\"john@mail.org\" }),\n new Contact({ name:\"Jane Doe\", email:\"jane@mail.org\" }),\n])\n\nconst janeEmail = 'jane@mail.org'\nconst jane = db.one\u003cContact\u003e($.from(Contact).where(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.email} = ${janeEmail}`))!\n\n// Insert examples\nconst { lastInsertRowid: bobId } = db.insert(new Contact({ name:\"Bob\", email:\"bob@mail.org\" }))\nconst { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`\nconst name = 'Alice', email = 'alice@mail.org'\ndb.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`\n\n// Typed SQL fragment with named param example\nconst hasId = \u003cTable extends { id:number }\u003e(id:number|bigint) =\u003e\n (x:Table) =\u003e $.sql( litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${x.id} = $id`, { id })\n\nconst contacts = db.all($.from(Contact).into(Contact)) // =\u003e Contact[]\nconst bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // =\u003e Contact \nconst contactsCount = db.value($.from(Contact).select`COUNT(*)`) // =\u003e number\nconst emails = db.column($.from(Contact).select(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.email}`)) // =\u003e string[]\nconst contactsArray = db.arrays($.from(Contact)) // =\u003e any[][]\nconst bobArray = db.array($.from(Contact).where(hasId(bobId))) // =\u003e any[]\n\n// Update examples\njane.email = 'jane@doe.org'\ndb.update(jane) // Update all properties\ndb.update(jane, { onlyProps:['email'] }) // Update only email\ndb.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder\n\n// Delete examples\ndb.delete(jane)\ndb.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder\n```\n\n\n# litdb for postgres.js\nSource: https://razor-press.web-templates.io/postgres\n\nUse litdb with [postgres.js](https://github.com/porsager/postgres) driver:\n\n:::sh\nnpm install @litdb/postgres\n:::\n\n## Configuration\n\n**db.ts**\n\n```ts\nimport { connect } from \"@litdb/postgres\"\n\nexport const connection = connect({ hostname, database, user, password })\nexport const { $, async:db, native:sql } = connection\n```\n\n:::tip\nWhen needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)\n:::\n\n### Configuration Options\n\n```ts\ntype ConnectionOptions = {\n /** Postgres ip address[s] or domain name[s] */\n host?: string | undefined;\n /** Postgres server[s] port[s] */\n port?: number | undefined;\n /** unix socket path (usually '/tmp') */\n path?: string | undefined;\n /** Password of database user (an alias for `password`) */\n pass?: Options\u003cT\u003e['password'] | undefined;\n /**\n * Password of database user\n * @default process.env['PGPASSWORD']\n */\n password?: string | (() =\u003e string | Promise\u003cstring\u003e) | undefined;\n /** Name of database to connect to (an alias for `database`) */\n db?: Options\u003cT\u003e['database'] | undefined;\n /** Username of database user (an alias for `user`) */\n username?: Options\u003cT\u003e['user'] | undefined;\n /** Postgres ip address or domain name (an alias for `host`) */\n hostname?: Options\u003cT\u003e['host'] | undefined;\n /**\n * Disable prepared mode\n * @deprecated use \"prepare\" option instead\n */\n no_prepare?: boolean | undefined;\n /**\n * Idle connection timeout in seconds\n * @deprecated use \"idle_timeout\" option instead\n */\n timeout?: Options\u003cT\u003e['idle_timeout'] | undefined;\n}\n```\n\nSee [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.\n\nExample:\n\n```ts\nconnection = connect({ hostname, database, user, password })\nconnection = connect(connectionString, options)\n```\n\n## Usage\n\nExample of using `@litdb/postgres` async APIs:\n\n```ts\nimport { $, db } from \"./db\"\nimport { Contact } from \"./models\"\n\nawait db.dropTable(Contact)\nawait db.createTable(Contact)\nawait db.insertAll([\n new Contact({ name:\"John Doe\", email:\"john@mail.org\" }),\n new Contact({ name:\"Jane Doe\", email:\"jane@mail.org\" }),\n])\n\nconst janeEmail = 'jane@mail.org'\nconst jane = await db.one\u003cContact\u003e($.from(Contact).where(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.email}=${janeEmail}`))\n\n// Insert examples\nconst { lastInsertRowid:bobId } = await db.insert(\n new Contact({ name:\"Bob\", email:\"bob@mail.org\"}))\n\nconst { lastInsertRowid } = await db.exec\n `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`\n\nconst name = 'Alice', email = 'alice@mail.org'\nawait db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`\n\n// Typed SQL fragment with named param example\nconst hasId = \u003cTable extends { id:number }\u003e(id:number|bigint) =\u003e\n (x:Table) =\u003e $.sql( litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${x.id} = $id`, { id })\n\nconst contacts = await db.all($.from(Contact).into(Contact)) // =\u003e Contact[]\nconst bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // =\u003e Contact\nconst contactsCount = await db.value($.from(Contact).rowCount()) // =\u003e number\nconst emails = await db.column($.from(Contact).select(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.email}`)) // =\u003e string[]\nconst contactsArray = await db.arrays($.from(Contact)) // =\u003e any[][]\nconst bobArray = await db.array($.from(Contact).where(hasId(bobId))) // =\u003e any[]\n\n// Update examples\njane.email = 'jane@doe.org'\nawait db.update(jane) // Update all properties\nawait db.update(jane, { onlyProps:['email'] }) // Update only email\n// query builder\nawait db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))\n\n// Delete examples\nawait db.delete(jane)\nawait db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder\n```\n\n\n# litdb for mysql2\nSource: https://razor-press.web-templates.io/mysql2\n\nUse litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:\n\n:::sh\nnpm install @litdb/mysql2\n:::\n\n## Configuration\n\nExample of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:\n\n**db.ts**\n\n```ts\nimport { connect } from \"@litdb/mysql2\"\n\nexport const connection = connect({ hostname, database, user, password })\nexport const { $, async:db, native:sql } = connection\n```\n\n:::tip\nWhen needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)\n:::\n\n### Configuration Options\n\n```ts\ntype ConnectionOptions = {\n // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`\n // default: false\n decimalNumbers?: boolean;\n // The MySQL user to authenticate as\n user?: string;\n // The password of that MySQL user\n password?: string;\n // Name of the database to use for this connection\n database?: string;\n // The charset for the connection.\n // default: UTF8_GENERAL_CI\n charset?: string;\n // The hostname of the database you are connecting to\n // default: localhost\n host?: string;\n // The port number to connect to\n // default: 3306\n port?: number;\n // The source IP address to use for TCP connection\n localAddress?: string;\n // The path to a unix domain socket to connect to. When used host and port are ignored\n socketPath?: string;\n // The timezone used to store local dates\n // default: local\n timezone?: string | 'local';\n}\n```\n\nSee [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.\n\nExample:\n\n```ts\nconnection = connect({ host, database, user, password })\nconnection = connect(connectionString)\n```\n\n## Usage\n\nExample of using `@litdb/mysql2` async APIs:\n\n```ts\nimport { $, db } from \"./db\"\nimport { Contact } from \"./models\"\n\nawait db.dropTable(Contact)\nawait db.createTable(Contact)\nawait db.insertAll([\n new Contact({ name:\"John Doe\", email:\"john@mail.org\" }),\n new Contact({ name:\"Jane Doe\", email:\"jane@mail.org\" }),\n])\n\nconst janeEmail = 'jane@mail.org'\nconst jane = await db.one\u003cContact\u003e($.from(Contact).where(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.email}=${janeEmail}`))\n\n// Insert examples\nconst { lastInsertRowid:bobId } = await db.insert(\n new Contact({ name:\"Bob\", email:\"bob@mail.org\"}))\n\nconst { lastInsertRowid } = await db.exec\n `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`\n\nconst name = 'Alice', email = 'alice@mail.org'\nawait db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`\n\n// Typed SQL fragment with named param example\nconst hasId = \u003cTable extends { id:number }\u003e(id:number|bigint) =\u003e\n (x:Table) =\u003e $.sql( litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${x.id} = $id`, { id })\n\nconst contacts = await db.all($.from(Contact).into(Contact)) // =\u003e Contact[]\nconst bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // =\u003e Contact\nconst contactsCount = await db.value($.from(Contact).rowCount()) // =\u003e number\nconst emails = await db.column($.from(Contact).select(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.email}`)) // =\u003e string[]\nconst contactsArray = await db.arrays($.from(Contact)) // =\u003e any[][]\nconst bobArray = await db.array($.from(Contact).where(hasId(bobId))) // =\u003e any[]\n\n// Update examples\njane.email = 'jane@doe.org'\nawait db.update(jane) // Update all properties\nawait db.update(jane, { onlyProps:['email'] }) // Update only email\n// query builder\nawait db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))\n\n// Delete examples\nawait db.delete(jane)\nawait db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder\n```\n\n\n# Schema APIs\nSource: https://razor-press.web-templates.io/schema\n\n:::info\nAll live examples use the data models defined in [/models](/models).\n:::\n\n## Create Table\n\n\u003clive-preview\u003e\nclass Freight {\n id = 0\n name = ''\n cost = 0.0\n}\nTable(Freight, {\n columns: {\n id: { type:'INTEGER', autoIncrement:true },\n name: { type:'TEXT', required:true, unique:true, index:true },\n cost: { type:'MONEY', required:true }\n }\n})\ndb.createTable(Freight)\ndb.createTable(Contact)\ndb.createTable(Order)\ndb.createTable(OrderItem)\ndb.createTable(Product)\n\u003c/live-preview\u003e\n\n## Drop Table\n\n\u003clive-preview\u003e\ndb.dropTable(Contact)\n\u003c/live-preview\u003e\n\n\n# SELECT Examples\nSource: https://razor-press.web-templates.io/select\n\n## Simple Queries\n\nSimple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the\nquery is expected to return multiple rows, a single row, a single value, a single column, etc.\n\n\u003clive-preview\u003e\nconst id = 1\ndb.all`SELECT * FROM Contact` // =\u003e Contact[]\ndb.one`SELECT * FROM Contact WHERE id = ${id}` // =\u003e Contact\ndb.value`SELECT COUNT(*) FROM Contact` // =\u003e number\ndb.column`SELECT name FROM Contact` // =\u003e string[]\ndb.arrays`SELECT * FROM Contact` // =\u003e any[][]\ndb.array`SELECT * FROM Contact WHERE id = ${id}` // =\u003e any[]\n\u003c/live-preview\u003e\n\n## SELECT Query Builder\n\n`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.\n\n\u003clive-preview\u003e\n$.from(Contact)\n\u003c/live-preview\u003e\n\n## Typed Selects\n\nWhen a custom select is needed you can use select function to specify the columns to select. All tables and columns\nusing typed references are automatically quoted.\n\n\u003clive-preview\u003e\n$.from(Contact).select(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.id}, ${c.name}, ${c.email}`)\n\u003c/live-preview\u003e\n\n## Aliases\n\nA table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to \ncreate a table reference.\n\n\u003clive-preview\u003e\ndb.all($.from(Contact,'c').select(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.id}, ${c.name}, ${c.email}`))\ndb.all($.from(Contact).as('c').select(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.id}, ${c.name}, ${c.age}`))\nconst c = $.ref(Contact,'c')\ndb.all($.from(c).select(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.id}, ${c.name}, ${c.createdAt}`))\n\u003c/live-preview\u003e\n\n## Select a list of Properties or Columns\n\nThe `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, \nwhilst the `columns` option can be used to select a list of RDBMS columns from the table.\n\n\u003clive-preview\u003e\ndb.all($.from(Contact).select({ props:['id', 'name', 'age'] }))\ndb.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))\n\u003c/live-preview\u003e\n\n\n# JOIN Examples\nSource: https://razor-press.web-templates.io/joins\n\n## Simple Join\n\nUse `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.\n\n\u003clive-preview\u003e\n$.from(Contact).join(Order, { on:(c,o) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.id} = ${o.contactId}` }).select('*')\n\u003c/live-preview\u003e\n\n## Custom Join Types\n\nUse `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.\n\n\u003clive-preview\u003e\ndb.all($.from(Contact).leftJoin(Order, { on:(c,o) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.id} = ${o.contactId}` }).select('*'))\ndb.all($.from(Contact).rightJoin(Order, { on:(c,o) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.id} = ${o.contactId}` }).select('*'))\ndb.all($.from(Contact).fullJoin(Order, { on:(c,o) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.id} = ${o.contactId}` }).select('*'))\ndb.all($.from(Contact).crossJoin(Order, { on:(c,o) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.id} = ${o.contactId}` }).select('*'))\n\u003c/live-preview\u003e\n\n## Multiple Joins\n\nMultiple joins can be chained together to create complex queries. A new query builder is created for each join \nthat's added containing references for all tables in the query in the order they were added. \n\nThese references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.\n\nThe `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include\na reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).\n\n\u003clive-preview\u003e\n$.from(Order)\n .leftJoin(Contact, { on:(o,c) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${o.contactId} = ${c.id}` })\n .join(OrderItem, { on:(_,i,o) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${i.orderId} = ${o.id}` })\n .leftJoin(Product, { on:(i,p) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${i.sku} = ${p.sku}` })\n .where((o,c,i,p) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.id} = ${1} AND ${p.cost} \u003e ${100}`)\n .select((o,c,i,p) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${o.id}, ${c.name}, ${i.qty}, ${p.name}`)\n\u003c/live-preview\u003e\n\n## Aliases\n\nEach joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.\n\n\u003clive-preview\u003e\n$.from(Order,'o')\n .leftJoin(Contact, { as:'c', on:(o,c) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${o.contactId} = ${c.id}` })\n .join(OrderItem, { as:'i', on:(_,i,o) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${i.orderId} = ${o.id}` })\n .leftJoin(Product, { as:'p', on:(i,p) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${i.sku} = ${p.sku}` })\n .where((o,c,i,p) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.id} = ${1} AND ${p.cost} \u003e ${100}`)\n .select((o,c,i,p) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${o.id}, ${c.name}, ${i.qty}, ${p.name}`)\n\u003c/live-preview\u003e\n\n## External References\n\nQueries can be joined on external references which can be used across multiple query builders that can be composed together \nto create complex queries that reference other queries.\n\n\u003clive-preview\u003e\nconst [ o, c, i, p ] = [ \n $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]\nconst recentOrder = $.from(Order,'o2')\n .where(o2 =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${o2.contactId} = ${c.id}`)\n .select(o2 =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


MAX(${o2.createdAt})`)\ndb.all($.from(o)\n .leftJoin(c, litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${o.contactId} = ${c.id}`)\n .join(i, litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${i.orderId} = ${o.id}`)\n .leftJoin(p, litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${i.sku} = ${p.sku}`)\n .where`${o.createdAt} = (${recentOrder})`\n .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)\n\u003c/live-preview\u003e\n\n## JOIN query builder\n\nWhen more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT\nquery builders to create complex queries.\n\n\u003clive-preview\u003e\n$.from(Contact,'c')\n .join(Order, { on:(c,o) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.id} = ${o.contactId}` })\n .join(\n $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) =\u003e \n litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)\n )\n .select('*')\n\u003c/live-preview\u003e\n\n## Cache complex JOIN queries\n\nFor improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated\ncloned query builders with `clone()`.\n\n\u003clive-preview\u003e\n const contactOrderItems = (() =\u003e {\n const q = $.from(Contact,'c')\n .join(Order, { as:'o', on:(c,o) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.id} = ${o.contactId}` })\n .join(OrderItem, { as:'i', on:(o,i) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${o.id} = ${i.orderId}` })\n return () =\u003e q.clone()\n})()\nconst [q1, q2, q3] = [...Array(3)].map(contactOrderItems)\nconst [ c, o, i ] = q1.refs\ndb.all(q1.where`${c.id} = ${10}`)\ndb.all(q2.where`${o.contactId} = ${20}`)\ndb.all(q3.where`${i.orderId} = ${100}`)\n\u003c/live-preview\u003e\n\n\n# WHERE Examples\nSource: https://razor-press.web-templates.io/where\n\n## Simple WHERE Clauses\n\nThe `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times \nto add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.\n\n\u003clive-preview\u003e\n$.from(Order)\n .leftJoin(Contact, { on:(o,c) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${o.contactId} = ${c.id}` })\n .where((o,c) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.id} = ${1} AND ${c.age} \u003e ${18}`)\n .or(o =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${o.total} \u003e ${100}`)\n .select('*')\n\u003c/live-preview\u003e\n\n## Array Expansion\n\nArrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.\n\n\u003clive-preview\u003e\n$.from(Contact).where`id IN (${[10,20,30]})`\n\u003c/live-preview\u003e\n\n## WHERE with Subqueries\n\nFragments can embed other fragments where their SQL and parameters are merged.\n\n\u003clive-preview\u003e\nconst hasPurchasesOver = (c,total) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


EXISTS (\n SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total \u003e= ${total})`\nconst inCity = (...cities) =\u003e c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.city} IN (${cities})`\nconst createdAfter = after =\u003e $.sql('createdAt \u003e= $after', { after })\nconst olderThan = age =\u003e ({ sql:'age \u003e= $age', params: { age } })\nconst q = $.from(Contact,'c')\n .where(c =\u003e hasPurchasesOver(c,1000))\n .and(inCity('Austin','Chicago'))\n .and(createdAfter(new Date('2024-01-01')))\n .and(olderThan(18))\n .and({ contains: { name:'John' } })\ndb.all(q)\n\u003c/live-preview\u003e\n\n### Subqueries with Query Builders\n\nSimilarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.\n\n\u003clive-preview src=\"/mjs/subselect.mjs\"\u003e\u003c/live-preview\u003e\n\n## WHERE convenience options\n\nThe `where` method can also be called with an object containing a number of convenience options to simplify creating\ncommon queries with an object query. If needed `op` can be used to create options for a custom SQL operator.\n\n\u003clive-preview\u003e\nconst search = {\n name: 'John',\n age: 27,\n city: 'Austin',\n}\ndb.all($.from(Contact).where({ equals: search }))\ndb.all($.from(Contact).where({ notEquals: search }))\ndb.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))\ndb.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))\ndb.all($.from(Contact).where({ op: ['\u003e=', { id:10, age:18 }] }))\n\u003c/live-preview\u003e\n\n### LIKE convenience options\n\nThe `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, \nend or any part of a string.\n\n\u003clive-preview\u003e\n$.from(Contact).where({ \n startsWith: { city:'A' }, \n contains: { email:'@gmail.' }, \n endsWith: { name:'J' }, \n})\n\u003c/live-preview\u003e\n\n### NULL check convenience options\n\nWhilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.\n\n\u003clive-preview\u003e\n$.from(Contact).where({ \n isNull: ['city', 'age'], \n notNull: ['email'], \n})\n\u003c/live-preview\u003e\n\n## Reset WHERE\n\nCalling `where` with no arguments will reset the WHERE clause:\n\n\u003clive-preview\u003e\n$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`\n\u003c/live-preview\u003e\n\n\n# GROUP BY Examples\nSource: https://razor-press.web-templates.io/group-by\n\n## Simple GROUP BY\n\n`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: \n\n\u003clive-preview\u003e\n$.from(Contact)\n .join(Order, { on:(c,o) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.id} = ${o.contactId}` })\n .groupBy(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.name}`)\n .select((c, o) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.name}, SUM(${o.total}) AS Total`)\n\u003c/live-preview\u003e\n\n## Multiple GROUP BY\n\nMultiple group by's can be added in one or multiple `groupBy` methods:\n\n\u003clive-preview\u003e\nconst q = $.from(Contact,'c')\n .join(Order, { on:(c,o) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.id} = ${o.contactId}` })\n .select((c, o) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.name}, ${c.city}, SUM(${o.total})`)\ndb.all(q.clone().groupBy(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.name}, ${c.city}`))\ndb.all(q.clone().groupBy(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.name}`).groupBy(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.city}`))\n\u003c/live-preview\u003e\n\n## GROUP BY Builder\n\nWhen more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently\nof the query:\n\n\u003clive-preview\u003e\n$.from(Contact,'c')\n .join(Order, { as:'o', on:(c,o) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.id} = ${o.contactId}` })\n .join(OrderItem, { as:'i', on:(o,i) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${o.id} = ${i.orderId}` })\n .groupBy(\n $.groupBy(Contact,OrderItem)\n .add(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.name}`)\n .add((_,i) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${i.sku}`)\n )\n .select((c,o,i) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.name}, ${i.sku}, SUM(${o.total}) AS total`)\n\u003c/live-preview\u003e\n\n## Reset GROUP BY\n\nCalling `groupBy` with no arguments will reset the GROUP BY clause:\n\n\u003clive-preview\u003e\n$.from(Contact).groupBy`name`.groupBy().select`name`\n\u003c/live-preview\u003e\n\n\n# HAVING Examples\nSource: https://razor-press.web-templates.io/having\n\n## Simple HAVING\n\nThe `having` method can be used to filter the results of a `groupBy` query:\n\n\u003clive-preview\u003e\n$.from(Contact)\n .join(Order, { on:(c,o) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.id} = ${o.contactId}` })\n .groupBy(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.city}`)\n .having(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


COUNT(${c.id}) \u003e 5`)\n .select(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.city}, COUNT(${c.id})`)\n\u003c/live-preview\u003e\n\n## Multiple HAVING\n\nMultiple having's can be added in one or multiple `having` methods:\n\n\u003clive-preview\u003e\nconst q = $.from(Contact)\n .join(Order, { on:(c,o) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.id} = ${o.contactId}` })\n .groupBy(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.city}`)\n .select(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.city}, COUNT(${c.id})`)\n \ndb.all(q.clone().having((c,o) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


COUNT(${c.id}) \u003e 5 AND SUM(${o.total}) \u003c 1000`))\ndb.all(q.clone()\n .having(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


COUNT(${c.id}) \u003e 5`).having((_,o) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


SUM(${o.total}) \u003c 1000`))\n\u003c/live-preview\u003e\n\n## HAVING Builder\n\nWhen more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently\nof the query:\n\n\u003clive-preview\u003e\n$.from(Contact)\n.join(Order, { on:(c,o) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.id} = ${o.contactId}` })\n.groupBy(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.city}`)\n.having(\n $.having(Contact,Order)\n .add(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


COUNT(${c.id}) \u003e 5`)\n .add((_,o) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


SUM(${o.total}) \u003c 1000`)\n).select(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.city}, COUNT(${c.id})`)\n\u003c/live-preview\u003e\n\n## Reset HAVING\n\nCalling `having` with no arguments will reset the ORDER BY clause:\n\n\u003clive-preview\u003e\n$.from(Contact).having`name`.having().select`name`\n\u003c/live-preview\u003e\n\n\n# ORDER BY Examples\nSource: https://razor-press.web-templates.io/order-by\n\n## Simple ORDER BY\n\nLike other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:\n\n\u003clive-preview\u003e\n$.from(Contact)\n .join(Order, { on:(c,o) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.id} = ${o.contactId}` })\n .select((c, o) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.name}, ${o.total}`)\n .orderBy(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.name}`)\n\u003c/live-preview\u003e\n\n## Multiple ORDER BY\n\nMultiple order by's can be added in one or multiple `orderBy` methods:\n\n\u003clive-preview\u003e\nconst q = $.from(Contact)\n .join(Order, { on:(c,o) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.id} = ${o.contactId}` })\n .select((c, o) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.name}, ${c.city}, ${o.total}`)\ndb.all(q.clone().orderBy(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.name}, ${c.city}`))\ndb.all(q.clone().orderBy(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.name}`).orderBy(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.city}`))\n\u003c/live-preview\u003e\n\n## ORDER BY Builder\n\nWhen more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently\nof the query:\n\n\u003clive-preview\u003e\n$.from(Contact,'c')\n .join(Order, { as:'o', on:(c,o) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.id} = ${o.contactId}` })\n .select((c, o) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.name}, ${o.total}`)\n .orderBy(\n $.orderBy(Contact,Order)\n .add(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.name}`)\n .add((_,o) =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${o.total} DESC`)\n )\n\u003c/live-preview\u003e\n\n## Reset ORDER BY\n\nCalling `orderBy` with no arguments will reset the ORDER BY clause:\n\n\u003clive-preview\u003e\n$.from(Contact).orderBy`name`.orderBy().select`name`\n\u003c/live-preview\u003e\n\n\n# INSERT Examples\nSource: https://razor-press.web-templates.io/insert\n\n## Simple INSERT\n\nInsert a new entity into a table using the `db.insert` driver method:\n\n\u003clive-preview\u003e\ndb.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))\ndb.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))\n\u003c/live-preview\u003e\n\n## INSERT Multiple Rows\n\nInsert multiple entities into a table using the `db.insertAll` driver method:\n\n\u003clive-preview\u003e\ndb.insertAll([\n new Contact({ name:'John', email:'john@email.com', age:27 }),\n new Contact({ name:'Jane', email:'jane@email.com', age:31 })\n])\n\u003c/live-preview\u003e\n\n## INSERT Expression\n\nWhen the full flexibility of SQL is needed, you can execute a SQL fragment directly:\n\n\u003clive-preview\u003e\nconst { name, age } = { name:'John', age:27 }\ndb.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`\n\u003c/live-preview\u003e\n\n\n# UPDATE Examples\nSource: https://razor-press.web-templates.io/update\n\n## Simple UPDATE\n\nSimple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:\n\n\u003clive-preview\u003e\ncontact = new Contact({ id:1, name:'John', email:'john@mail.org' })\ndb.update(contact)\n\u003c/live-preview\u003e\n\n## UPDATE Specific Properties\n\nFor updating specific properties of a data model, the `onlyProps` option can be used:\n\n\u003clive-preview\u003e\ndb.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })\n\u003c/live-preview\u003e\n\n## UPDATE Query Builder\n\nWhen more flexibility is needed you can use `$.update()` to create an UPDATE query builder:\n\n\u003clive-preview\u003e\ndb.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))\nconst { age, city, email } = { age:41, email:'john@mail.org' }\nconst q = $.update(Contact)\nif (age) q.set({ age })\nif (city) q.set({ city })\nif (email) q.set({ email })\ndb.run(q.where($.idEquals(1)))\n\u003c/live-preview\u003e\n\n## UPDATE Expression\n\nWhen the full flexibility of SQL is needed, you can execute a SQL fragment directly:\n\n\u003clive-preview\u003e\nconst { id, name, age } = { id:1, name:'John', age:27 }\ndb.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`\n\u003c/live-preview\u003e\n\n\n# DELETE Examples\nSource: https://razor-press.web-templates.io/delete\n\n## Simple DELETE\n\nDelete an entity using the `db.delete` driver method:\n\n\u003clive-preview\u003e\ndb.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))\n\u003c/live-preview\u003e\n\n## DELETE Query Builder\n \nWhen a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:\n\n\u003clive-preview\u003e\nconst yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))\ndb.run($.deleteFrom(Order).where(c =\u003e litdb.dev | Top Sites | DialtoneApp

Top Siteslitdb - type safe SQL for TypeScript/JavaScript

Machine Readiness

Stored receipt and evidence

Overall

24

Readable

80

Callable

0

Commerce

0

Payment

0

Machine Access

Inspect the site's MCP endpoint

Open MCP explorer

DialtoneApp can scan the stored discovery files for this domain, try the MCP initialize handshake, and show the raw protocol transcript.

Purchase boundary

read only

Control boundary

unknown

Payment rails

None

Payment providers

None

Payment methods

None

Payment protocols

None

Payment assets

None

Payment networks

None

Capabilities

None

Verified payment surface

No

Crypto only

No

Readable docs

llms, llms-full

Products

0

Variants

0

Priced variants

0

Currencies

0

Offers

0

Priced offers

0

Priced actions

0

Samples

Offer samples

No stored offer samples.

Samples

Action samples

No stored action samples.

Samples

Product samples

No stored product samples.

Document

robots.txt

Not stored for this site.

Document

llms.txt

Open llms.txt
# litdb

## Docs

Home
 - [Overview](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/overview.md)
 - [Install](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/install.md)
 - [Models](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/models.md)
 - [Customize](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/customize.md)
 - [Report Issue](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bug-report.md): Creating a good Bug Report

Drivers
 - [SQLite (Bun)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/bun-sqlite.md): litdb for Bun SQLite
 - [SQLite (Node.js)](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/better-sqlite.md): litdb for Node.js better-sqlite3
 - [PostgreSQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/postgres.md): litdb for postgres.js
 - [MySQL](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/mysql2.md): litdb for mysql2

Queries
 - [Schema](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/schema.md): Schema APIs
 - [Select](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/select.md): SELECT Examples
 - [Joins](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/joins.md): JOIN Examples
 - [Where](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/where.md): WHERE Examples
 - [Group By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/group-by.md): GROUP BY Examples
 - [Having](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/having.md): HAVING Examples
 - [Order By](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/order-by.md): ORDER BY Examples
 - [Insert](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/insert.md): INSERT Examples
 - [Update](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/update.md): UPDATE Examples
 - [Delete](https://raw.githubusercontent.com/litdb/litdb.dev/refs/heads/main/MyApp/_pages/delete.md): DELETE Examples

Document

llms-full.txt

Open llms-full.txt
# Overview
Source: https://razor-press.web-templates.io/overview

## What is litdb?

litdb is a suite of simple and lightweight database agnostic abstractions and SQL query builders for TypeScript and JavaScript. 
It is designed to leverage TypeScript's powerful type system to provide a simple and intuitive type-safe wrapper around
constructing and executing typed SQL queries with a focus on type safety, best practices and portability.

### litdb library

The core `litdb` library provides a set of composable query builders and SQL fragments that can be used to generate
SQL that can be executed on SQLite, MySQL and PostgreSQL.

### SQL Expression

The `


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


   tagged template function is used to create parameterized SQL Fragments that's split into `sql` and `params`:

```ts
type Fragment = { sql:string, params:Record<string,any> }
```

<live-preview>
JSON.stringify(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  id = ${1} OR name = ${'John'}`)
</live-preview>

### SQL Builder

SQL Builders are just objects containing a `build()` function which returns an SQL `Fragment`:

```ts
interface SqlBuilder {
    build(): Fragment
}
```

Their simplicity and loose coupling allows them to be used in any ORM or driver that can execute parameterized SQL.

### litdb drivers

The litdb Drivers provide a unified interface for executing custom parameterized SQL, SQL Builders and SQL Fragments 
for their respective RDBMS. The SQLite drivers support both the Sync and Async DbConnection whilst
remote databases like PostgreSQL and MySQL only support the Async DbConnection.

```ts
interface SyncDbConnection {
    driver:Driver
    $:Function
    schema:Schema
    quote(symbol:string): string
    insert<T extends ClassInstance>(row:T, options?:InsertOptions): Changes
    insertAll<T extends ClassInstance>(rows:T[], options?:InsertOptions): Changes
    update<T extends ClassInstance>(row:T, options?:UpdateOptions): Changes
    delete<T extends ClassInstance>(row:T, options?:DeleteOptions): Changes
    listTables(): string[]
    dropTable<Table extends ClassParam>(table:Table): void
    createTable<Table extends ClassParam>(table:Table): void
    all<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType[]
    one<RetType>(strings: TemplateStringsArray | SqlBuilder | Fragment | IntoFragment<RetType>, ...params: any[]): RetType
    column<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): RetType[]
    value<ReturnValue>(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): ReturnValue
    arrays(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[][]
    array(strings: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): any[]
    exec(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): Changes
    run(strings:TemplateStringsArray | SqlBuilder | Fragment, ...params:any[]): void
    prepareSync<T>(str: TemplateStringsArray | SqlBuilder | Fragment, ...params: any[]): Statement    
    close()
}

type Changes = { changes: number; lastInsertRowid: number | bigint }
```

## Data Models

litdb is more a lightweight data mapper than a full-fledged ORM, but many of its APIs are designed to work with 
[data models](/models) which are simple TypeScript classes that represent and map 1:1 to tables in a database.

## Safe by default

All SQL Queries and SQL Fragments require using a tagged template function or SQL Builder which parameterizes 
all values to prevent SQL Injection attacks, as such accidentally using a `string` will result in an error, e.g:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one(`SELECT * FROM Contact WHERE name = '${bobbyTables}'`)
</live-preview>

Driver APIs, SQL Builders and Expressions instead accept templated strings which auto parameterizes SQL queries:

<live-preview>
const bobbyTables = "Robert'); DROP TABLE Students;--"
db.one($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name} = ${bobbyTables}`))
db.one($.from(Contact).where`name = ${bobbyTables}`)
db.one`SELECT * FROM Contact WHERE name = ${bobbyTables}`
db.one(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SELECT * FROM Contact WHERE name = ${bobbyTables}`)
db.one($.sql('SELECT * FROM Contact WHERE name = $bobbyTables', { bobbyTables }))
db.one({ sql:'SELECT * FROM Contact WHERE name = $bobbyTables', params:{bobbyTables} })
</live-preview>

## Portable

litdb is designed to be portable where in most cases query builders, expressions and driver APIs that stick to ANSI SQL
syntax can be used across different databases and drivers. This preserves investments and knowledge reuse allowing
your App's logic to bind to RDBMS-agnostic abstractions that can be reused across different databases or allow your 
App to easily migrate to run on different databases.

## Expressive

At the same time litdb SQL Builders and Fragments doesn't restrict you to a subset of SQL, instead of forcing the use of
a more restrictive query language that abstracts away the full power of SQL, litdb's SQL Builders are designed for creating
type-safe parameterized SQL that can be executed on any RDBMS, but when needed you can use the full feature-set of 
your RDBMS SQL dialect to make use of any RDBMS-specific features.


# Install
Source: https://razor-press.web-templates.io/install

To use litdb with your favorite ORM, no driver is required. Just use the `litdb` package directly:

:::sh
npm install litdb
:::

`litdb` is also available as a module, where it can be used directly in the browser:

```html
<script type="module">
import { sqlite as $ } from "https://unpkg.com/litdb/dist/index.min.js"
const { sql, params } = $.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).build()
</script>
```

To get the most out of `litdb` we recommend using text editors that supports TypeScript definitions (e.g. VS Code)

# LitDB Drivers

Lightweight drivers with first-class support for litdb query builders are also available for the popular databases below:

### SQLite (Bun)

Use with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

See [litdb Bun SQLite Docs](/bun-sqlite).

### SQLite (Node.js)

Use with Node [better-sqlite3](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

See [litdb better-sqlite3 Docs](/better-sqlite).

### PostgreSQL

Use with the [postgres.js](https://github.com/porsager/postgres) client:

:::sh
npm install @litdb/postgres
:::

See [litdb postgres Docs](/postgres).

### MySQL

Use with the [mysql2](https://github.com/sidorares/node-mysql2) client:

:::sh
npm install @litdb/mysql2
:::

See [litdb mysql2 Docs](/mysql2).

### Request a Driver

If you'd like to see a driver for a specific client, please open or vote for a feature request on litdb's 
[GitHub Discussions](https://github.com/litdb/litdb/discussions/categories/ideas).

## Driver Usage

litdb drivers are lightweight data adapters providing convenience APIs for executing SQL and parameters. 
They can be used with or without litdb SQL Builders, but offer the most value when used together. 

The same APIs are available across all drivers, so you can easily switch between them. They include both **sync** APIs
recommended for SQLite libraries that use SQLite's native blocking APIs, whilst **async** APIs should be used for 
querying all other remote databases, e.g. PostgreSQL and MySQL.


# Models
Source: https://razor-press.web-templates.io/models

Model definitions are a representation of your RDBMS tables and columns in your App's code that's used to
configure how your App's classes and properties map to your database tables and columns.

They're used for creating table schemas and is able to influence the SQL that's generated by query builders 
and how results are mapped between your data models and RDBMS.

They can be defined using a Fluent API to configure existing classes or by using JavaScript decorators to declaratively 
annotate classes. In both cases litdb's TypeScript definitions provide intelli-sense to assist you in annotating your models. 

## Fluent API

```ts
import { Table } from 'litdb'

export class Contact {
    constructor(data) { Object.assign(this, data) }
    id = 0
    name = ''
    age = 0 || undefined
    email = ''
    city = '' || undefined
    createdAt = new Date(2025,1,1)
}
export class Order {
    constructor(data) { Object.assign(this, data) }
    id = 0
    contactId = 0
    total = 0.0
    createdAt = new Date()
}
export class OrderItem {
    constructor(data) { Object.assign(this, data) }
    id = 0
    orderId = 0
    sku = ''
    qty = 0
    total = 0.0
}
export class Product {
    constructor(data) { Object.assign(this, data) }
    sku = ''
    name = ''
    cost = 0.0
}

Table(Contact, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        name:      { type:"TEXT",     required:true },
        age:       { type:"INTEGER" },
        email:     { type:"TEXT",     required:true, index:true, unique:true },
        city:      { type:"TEXT" },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(Order, {
    columns: {
        id:        { type:"INTEGER",  autoIncrement:true },
        contactId: { type:"INTEGER",  required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } },
        total:     { type:"MONEY",    required:true },
        createdAt: { type:"DATETIME", defaultValue:"CURRENT_TIMESTAMP" },
    }
})
Table(OrderItem, {
    columns: {
        id:      { type:"INTEGER", autoIncrement:true },
        orderId: { type:"INTEGER", required:true, references:{ table:Order,   on:["DELETE","RESTRICT"] } },
        sku:     { type:"TEXT",    required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } },
        qty:     { type:"INTEGER", required:true },
        total:   { type:"MONEY",   required:true }
    }
})
Table(Product, {
    columns: {
        sku:  { type:"TEXT",  primaryKey:true },
        name: { type:"TEXT",  required:true, index:true, unique:true },
        cost: { type:"MONEY", required:true },
    }
})
```

## Declarative Annotations

TypeScript or JS build systems that support [TC39 decorators](https://github.com/tc39/proposal-decorators) can use the 
`@table` and `@column` decorators to define their data models, e.g: 

```ts
import { table, column, DefaultValues } from 'litdb'

@table()
export class Contact {
    constructor(data?: Partial<Contact>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement: true })
    id = 0
    
    @column("TEXT", { required: true })
    name = ''
    
    @column("INTEGER")
    age?: number

    @column("TEXT", { required:true, index:true, unique:true })
    email = ''

    @column("TEXT")
    city = ''
    
    @column("DATETIME", { defaultValue:'CURRENT_TIMESTAMP' })
    createdAt = new Date()
}

@table()
export class Order {
    constructor(data?: Partial<Order>) { Object.assign(this, data) }

    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Contact, on:["DELETE","CASCADE"] } })
    contactId: number = 0

    @column("MONEY", { required:true})
    total: number = 0

    @column("DATETIME", { defaultValue:DefaultValues.NOW })
    createdAt = new Date()
}

@table()
export class OrderItem {
    @column("INTEGER", { autoIncrement:true })
    id: number = 0

    @column("INTEGER", { required:true, references:{ table:Order, on:["DELETE","RESTRICT"] } })
    orderId: number = 0

    @column("TEXT", { required:true, references:{ table:Product, on:["DELETE","RESTRICT"] } })
    sku: string = ''

    @column("INTEGER", { required:true })
    qty: number = 0

    @column("MONEY", { required:true })
    total: number = 0
}

@table()
export class Product {
    @column("TEXT", { primaryKey:true })
    sku = ''
    @column("TEXT", { required:true, index:true, unique:true })
    name = ''
    @column("MONEY", { required:true })
    cost = 0.0
}
```

### Custom Data Types

When needed, a `Symbol` can be used to define custom data types, e.g:

```ts
class Address {
    @column(Symbol("POINT"))
    location
}
```


# Customize
Source: https://razor-press.web-templates.io/customize

## Custom Naming Strategy

App's can configure litdb to use a custom naming strategy for tables and columns by configuring the dialect's strategy. 

```ts
import { SnakeCaseStrategy } from "litdb"
import { connect } from "@litdb/postgres"

export const connection = connect({hostname, database, user, password})
export const { $, async: db, native:sql } = connection
connection.dialect.strategy = new SnakeCaseStrategy()
```

Where SnakeCaseStrategy is defined as returning table and column names in snake_case:

```ts
class SnakeCaseStrategy {
    tableName(table:string) : string { return snakeCase(table) }
    columnName(column:string) : string { return snakeCase(column) }
}
```

## Type Converters

litdb uses type converters to convert between JavaScript Objects and RDBMS data types. App's can configure litdb to use 
custom type converters by registering them with the driver's schema.

A Type Converter is an object with `toDb` and `fromDb` methods that convert between JavaScript and RDBMS data types.

```ts
interface TypeConverter {
    toDb(value: any): any;
    fromDb(value: any): any;
}
```

For example, this `DateTimeConverter` is used to convert `Date` objects to and from MySQL's `DATETIME` data type:

```ts
class DateTimeConverter implements TypeConverter
{
    toDb(value: any) {
        const d = toDate(value)
        return d ? dateISOString(d).replace('T',' ') : null
    }
    fromDb(value: any) {
        if (!value) return null
        return toDate(value)
    }
}
```

Custom Type Converters can be registered with the driver's schema for the data type it should apply to:

```ts
export const connection = connect({ host, database, user, password })
export const { $, async:db, native } = connection

connection.schema.converters['DATETIME'] = new DateTimeConverter()
```

## Register Converter for Mutliple Data Types

The `converterFor` utility function can be used to register a converter for multiple data types:

```ts
import { converterFor } from "litdb"

Object.assign(connection.schema.converters, 
    converterFor(new DateConverter(), "DATE", "DATETIME", "TIMESTAMP", "TIMESTAMPZ"))
```


# Creating a good Bug Report
Source: https://razor-press.web-templates.io/bug-report

### [View existing Issues](https://github.com/litdb/litdb/issues) or [Report a New Issue](https://github.com/litdb/litdb/issues/new?template=bug_report.yml)

> Issues must be reproducible with either a failing test, sample code, gist, link to a stand-alone project 
or otherwise clear steps to reproduce the issue. For details please see 
[How to create a Minimal, Complete, and Verifiable example](http://stackoverflow.com/help/mcve).


The more effective your bug report is, the better chance it will get fixed. So fixing a bug depends on how
well it's reported.

## 1) Reproducible:

If your bug is not reproducible it will never get fixed. You should clearly **provide the steps to reproduce the bug**.
Do not assume or skip any reproducing step. A Step-by-step description of the issue is easy to reproduce and fix.
E.g. **A failing test** with the issue (or in a gist) is the preferred way to report a reproducible error as it contains
all the assumptions and environment settings necessary for the error to occur.

## 2) Be Specific:

Do not write a essay about the problem. Be Specific and to the point. Try to summarize the problem in minimum
words yet in effective way. Do not combine multiple problems even they seem to be similar.
Write different reports for each problem.

## 3) Environment Details:

Ensure you're using the latest litdb packages, include the Operating System and the versions of the relevant
major components, e.g. JS Runtime (Node.js, Bun, Deno), etc. If you're using a browser, mention the browser and its version.


# litdb for Bun SQLite
Source: https://razor-press.web-templates.io/bun-sqlite

Use litdb with [Bun's native SQLite3 driver](https://bun.sh/docs/api/sqlite) (requires Bun):

:::sh
bun install @litdb/bun-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/bun-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access underlying [bun:sqlite Database](https://bun.sh/docs/api/sqlite#database) 
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default "app.db"
    fileName?:string
    // Whether to enable WAL
    // default true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    readonly?: boolean
    // Allow creating a new database
    create?: boolean;
    // Open the database as read-write
    readwrite?: boolean;
    // When set to `true`, integers are returned as `bigint` types.
    // When set to `false`, integers are returned as `number` types and truncated to 52 bits.
    // default false
    safeIntegers?: boolean;
    // When set to `false` or `undefined`:
    // - Queries missing bound parameters will NOT throw an error
    // - Bound named parameters in JavaScript need to exactly match the SQL query.
    // default true
    strict?: boolean;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/bun-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for Node.js better-sqlite3
Source: https://razor-press.web-templates.io/better-sqlite

Use litdb with the [better-sqlite3 driver](https://github.com/WiseLibs/better-sqlite3) (requires Node.js):

:::sh
npm install @litdb/better-sqlite
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/better-sqlite"

export const connection = connect("app.db") // WAL enabled by default
export const { $, sync:db, async, native } = connection
```

:::tip
When needed use `native` to access the native [better-sqlite3 Database](https://github.com/WiseLibs/better-sqlite3/blob/master/docs/api.md#class-database)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // Creates a new database connection to the specified SQLite DB. 
    // If the database file does not exist, it is created.
    // default: app.db
    fileName?:string
    // Whether to enable WAL
    // default: true
    wal?:boolean
    // Open the database as read-only (no write operations, no create).
    // default: false
    readonly?: boolean
    // If the database does not exist, an Error will be thrown instead of creating a new file
    fileMustExist?: boolean | undefined;
    // The number of milliseconds to wait when executing queries on a locked database, 
    // before throwing a SQLITE_BUSY error
    // default: 5000
    timeout?: number | undefined;
    // Provide a function that gets called with every SQL string executed by the db connection
    verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
    // If you're using a build system that moves, transforms, or concatenates your JS files,
    // you can solve it by using this option to provide the file path of better_sqlite3.node 
    // (relative to the current working directory).
    nativeBinding?: string | undefined;
}
```

Example:

```ts
export const connection = connect({ fileName:'app.db' })
```

## Usage

Example of using `@litdb/better-sqlite` sync APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

db.dropTable(Contact)
db.createTable(Contact)
db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email} = ${janeEmail}`))!

// Insert examples
const { lastInsertRowid: bobId } = db.insert(new Contact({ name:"Bob", email:"bob@mail.org" }))
const { lastInsertRowid } = db.exec`INSERT INTO Contact(name,email) VALUES ('Jo','jo@doe.org')`
const name = 'Alice', email = 'alice@mail.org'
db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact    
const contactsCount = db.value($.from(Contact).select`COUNT(*)`)      // => number
const emails = db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = db.arrays($.from(Contact))                      // => any[][]
const bobArray = db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
db.update(jane)                           // Update all properties
db.update(jane, { onlyProps:['email'] })  // Update only email
db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id))) // query builder

// Delete examples
db.delete(jane)
db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for postgres.js
Source: https://razor-press.web-templates.io/postgres

Use litdb with [postgres.js](https://github.com/porsager/postgres) driver:

:::sh
npm install @litdb/postgres
:::

## Configuration

**db.ts**

```ts
import { connect } from "@litdb/postgres"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `sql` to access the native [postgres.js sql function](https://github.com/porsager/postgres#usage)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    /** Postgres ip address[s] or domain name[s] */
    host?: string | undefined;
    /** Postgres server[s] port[s] */
    port?: number | undefined;
    /** unix socket path (usually '/tmp') */
    path?: string | undefined;
    /** Password of database user (an alias for `password`) */
    pass?: Options<T>['password'] | undefined;
    /**
     * Password of database user
     * @default process.env['PGPASSWORD']
     */
    password?: string | (() => string | Promise<string>) | undefined;
    /** Name of database to connect to (an alias for `database`) */
    db?: Options<T>['database'] | undefined;
    /** Username of database user (an alias for `user`) */
    username?: Options<T>['user'] | undefined;
    /** Postgres ip address or domain name (an alias for `host`) */
    hostname?: Options<T>['host'] | undefined;
    /**
     * Disable prepared mode
     * @deprecated use "prepare" option instead
     */
    no_prepare?: boolean | undefined;
    /**
     * Idle connection timeout in seconds
     * @deprecated use "idle_timeout" option instead
     */
    timeout?: Options<T>['idle_timeout'] | undefined;
}
```

See [postgres.js Connection Options](https://github.com/porsager/postgres?tab=readme-ov-file#connection-details) for more.

Example:

```ts
connection = connect({ hostname, database, user, password })
connection = connect(connectionString, options)
```

## Usage

Example of using `@litdb/postgres` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# litdb for mysql2
Source: https://razor-press.web-templates.io/mysql2

Use litdb with [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

:::sh
npm install @litdb/mysql2
:::

## Configuration

Example of using the [node-mysql2](https://github.com/sidorares/node-mysql2) driver:

**db.ts**

```ts
import { connect } from "@litdb/mysql2"

export const connection = connect({ hostname, database, user, password })
export const { $, async:db, native:sql } = connection
```

:::tip
When needed use `native` to access [mysql2 Connection Pool](https://sidorares.github.io/node-mysql2/docs#using-connection-pools)
:::

### Configuration Options

```ts
type ConnectionOptions = {
    // DECIMAL and NEWDECIMAL types will be returned as numbers if this option is set to `true`
    // default: false
    decimalNumbers?: boolean;
    // The MySQL user to authenticate as
    user?: string;
    // The password of that MySQL user
    password?: string;
    // Name of the database to use for this connection
    database?: string;
    // The charset for the connection.
    // default: UTF8_GENERAL_CI
    charset?: string;
    // The hostname of the database you are connecting to
    // default: localhost
    host?: string;
    // The port number to connect to
    // default: 3306
    port?: number;
    // The source IP address to use for TCP connection
    localAddress?: string;
    // The path to a unix domain socket to connect to. When used host and port are ignored
    socketPath?: string;
    // The timezone used to store local dates
    // default: local
    timezone?: string | 'local';
}
```

See [node-mysql Connection Pool Options](https://sidorares.github.io/node-mysql2/docs#using-connection-pools) for more.

Example:

```ts
connection = connect({ host, database, user, password })
connection = connect(connectionString)
```

## Usage

Example of using `@litdb/mysql2` async APIs:

```ts
import { $, db } from "./db"
import { Contact } from "./models"

await db.dropTable(Contact)
await db.createTable(Contact)
await db.insertAll([
    new Contact({ name:"John Doe", email:"john@mail.org" }),
    new Contact({ name:"Jane Doe", email:"jane@mail.org" }),
])

const janeEmail = 'jane@mail.org'
const jane = await db.one<Contact>($.from(Contact).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}=${janeEmail}`))

// Insert examples
const { lastInsertRowid:bobId } = await db.insert(
    new Contact({ name:"Bob", email:"bob@mail.org"}))

const { lastInsertRowid } = await db.exec
    `INSERT INTO Contact(name,email) VALUES('Jo','jo@doe.org')`

const name = 'Alice', email = 'alice@mail.org'
await db.exec`INSERT INTO Contact(name,email) VALUES (${name}, ${email})`

// Typed SQL fragment with named param example
const hasId = <Table extends { id:number }>(id:number|bigint) =>
    (x:Table) => $.sql(


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${x.id} = $id`, { id })

const contacts = await db.all($.from(Contact).into(Contact))                // => Contact[]
const bob = await db.one($.from(Contact).where(hasId(bobId)).into(Contact)) // => Contact
const contactsCount = await db.value($.from(Contact).rowCount())            // => number
const emails = await db.column($.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.email}`))  // => string[]
const contactsArray = await db.arrays($.from(Contact))                      // => any[][]
const bobArray = await db.array($.from(Contact).where(hasId(bobId)))        // => any[]

// Update examples
jane.email = 'jane@doe.org'
await db.update(jane)                           // Update all properties
await db.update(jane, { onlyProps:['email'] })  // Update only email
// query builder
await db.exec($.update(Contact).set({ email:jane.email }).where(hasId(jane.id)))

// Delete examples
await db.delete(jane)
await db.exec($.deleteFrom(Contact).where(hasId(jane.id))) // query builder
```


# Schema APIs
Source: https://razor-press.web-templates.io/schema

:::info
All live examples use the data models defined in [/models](/models).
:::

## Create Table

<live-preview>
class Freight {
  id = 0
  name = ''
  cost = 0.0
}
Table(Freight, {
  columns: {
    id: { type:'INTEGER', autoIncrement:true },
    name: { type:'TEXT', required:true, unique:true, index:true },
    cost: { type:'MONEY', required:true }
  }
})
db.createTable(Freight)
db.createTable(Contact)
db.createTable(Order)
db.createTable(OrderItem)
db.createTable(Product)
</live-preview>

## Drop Table

<live-preview>
db.dropTable(Contact)
</live-preview>


# SELECT Examples
Source: https://razor-press.web-templates.io/select

## Simple Queries

Simple queries can be executed directly on the litdb driver APIs, the different APIs available based on whether the
query is expected to return multiple rows, a single row, a single value, a single column, etc.

<live-preview>
const id = 1
db.all`SELECT * FROM Contact`                     // => Contact[]
db.one`SELECT * FROM Contact WHERE id = ${id}`    // => Contact
db.value`SELECT COUNT(*) FROM Contact`            // => number
db.column`SELECT name FROM Contact`               // => string[]
db.arrays`SELECT * FROM Contact`                  // => any[][]
db.array`SELECT * FROM Contact WHERE id = ${id}`  // => any[]
</live-preview>

## SELECT Query Builder

`$.from(Table)` is used to create a SELECT query builder which by default selects all known columns of the data model.

<live-preview>
$.from(Contact)
</live-preview>

## Typed Selects

When a custom select is needed you can use select function to specify the columns to select. All tables and columns
using typed references are automatically quoted.

<live-preview>
$.from(Contact).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`)
</live-preview>

## Aliases

A table alias can be specified in `$.from()`, with the `.as()` method or by using `$.ref()` to 
create a table reference.

<live-preview>
db.all($.from(Contact,'c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.email}`))
db.all($.from(Contact).as('c').select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.age}`))
const c = $.ref(Contact,'c')
db.all($.from(c).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id}, ${c.name}, ${c.createdAt}`))
</live-preview>

## Select a list of Properties or Columns

The `props` option can be used to select a list of properties from the data model where any aliases would be used if defined, 
whilst the `columns` option can be used to select a list of RDBMS columns from the table.

<live-preview>
db.all($.from(Contact).select({ props:['id', 'name', 'age'] }))
db.all($.from(Contact).select({ columns:['id', 'name', 'email'] }))
</live-preview>


# JOIN Examples
Source: https://razor-press.web-templates.io/joins

## Simple Join

Use `join` to create a new query builder with an additional table join. The `on` option is used to specify the join condition.

<live-preview>
$.from(Contact).join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*')
</live-preview>

## Custom Join Types

Use `leftJoin`, `rightJoin`, `fullJoin`, `crossJoin` to create a new query builder with a specific join type.

<live-preview>
db.all($.from(Contact).leftJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).rightJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).fullJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
db.all($.from(Contact).crossJoin(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` }).select('*'))
</live-preview>

## Multiple Joins

Multiple joins can be chained together to create complex queries. A new query builder is created for each join 
that's added containing references for all tables in the query in the order they were added. 

These references can be used in `where`, `select`, `groupBy`, `orderBy` methods to reference columns from each table.

The `*join` APIs are instead passed a reference to the **previous** joined table and the **current** table, they also include
a reference to the **primary** table as the last reference (e.g. OrderItem `on:(_,i,o)`).

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## Aliases

Each joined table can be assigned an alias using the `as` option. This alias is then used to reference the table in the query.

<live-preview>
$.from(Order,'o')
  .leftJoin(Contact, { as:'c', on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .join(OrderItem,   { as:'i', on:(_,i,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}` })
  .leftJoin(Product, { as:'p', on:(i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}` })
  .where((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${p.cost} > ${100}`)
  .select((o,c,i,p) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## External References

Queries can be joined on external references which can be used across multiple query builders that can be composed together 
to create complex queries that reference other queries.

<live-preview>
const [ o, c, i, p ] = [ 
  $.ref(Order,'o'), $.ref(Contact,'c'), $.ref(OrderItem,'i'), $.ref(Product,'p') ]
const recentOrder = $.from(Order,'o2')
  .where(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o2.contactId} = ${c.id}`)
  .select(o2 => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  MAX(${o2.createdAt})`)
db.all($.from(o)
  .leftJoin(c, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}`)
  .join(i, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.orderId} = ${o.id}`)
  .leftJoin(p, 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku} = ${p.sku}`)
  .where`${o.createdAt} = (${recentOrder})`
  .select`${o.id}, ${c.name}, ${i.qty}, ${p.name}`)
</live-preview>

## JOIN query builder

When more flexibility is needed you can create a JOIN query builder with `$.join()` that can be added to other SELECT
query builders to create complex queries.

<live-preview>
$.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(
       $.join(OrderItem,Order,Product).as('i').leftJoin((i, o, p) => 
         


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId} LEFT JOIN ${p} ON ${i.sku} = ${p.sku}`)
    )
    .select('*')
</live-preview>

## Cache complex JOIN queries

For improved performance and to simplify complex queries, complex joins can be reused and memoized by returning isolated
cloned query builders with `clone()`.

<live-preview>
 const contactOrderItems = (() => {
    const q = $.from(Contact,'c')
        .join(Order,     { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
        .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    return () => q.clone()
})()
const [q1, q2, q3] = [...Array(3)].map(contactOrderItems)
const [ c, o, i ] = q1.refs
db.all(q1.where`${c.id} = ${10}`)
db.all(q2.where`${o.contactId} = ${20}`)
db.all(q3.where`${i.orderId} = ${100}`)
</live-preview>


# WHERE Examples
Source: https://razor-press.web-templates.io/where

## Simple WHERE Clauses

The `where` method is used to add a WHERE clause to the query, it's an alias for `and` which can be called multiple times 
to add multiple **AND** conditions to the WHERE clause, whilst `or` can be used to add an **OR** condition.

<live-preview>
$.from(Order)
  .leftJoin(Contact, { on:(o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.contactId} = ${c.id}` })
  .where((o,c) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${1} AND ${c.age} > ${18}`)
  .or(o => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} > ${100}`)
  .select('*')
</live-preview>

## Array Expansion

Arrays embedded in SQL Fragments are expanded into a list of parameters, this can be used to create IN clauses.

<live-preview>
$.from(Contact).where`id IN (${[10,20,30]})`
</live-preview>

## WHERE with Subqueries

Fragments can embed other fragments where their SQL and parameters are merged.

<live-preview>
const hasPurchasesOver = (c,total) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  EXISTS (
       SELECT 1 FROM Order WHERE o.contactId = ${c.id} AND total >= ${total})`
const inCity = (...cities) => c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city} IN (${cities})`
const createdAfter = after => $.sql('createdAt >= $after', { after })
const olderThan = age => ({ sql:'age >= $age', params: { age } })
const q = $.from(Contact,'c')
    .where(c => hasPurchasesOver(c,1000))
    .and(inCity('Austin','Chicago'))
    .and(createdAfter(new Date('2024-01-01')))
    .and(olderThan(18))
    .and({ contains: { name:'John' } })
db.all(q)
</live-preview>

### Subqueries with Query Builders

Similarly, Query Builders and SQL Fragments can be embedded in other Query Builders to create complex subqueries.

<live-preview src="/mjs/subselect.mjs"></live-preview>

## WHERE convenience options

The `where` method can also be called with an object containing a number of convenience options to simplify creating
common queries with an object query. If needed `op` can be used to create options for a custom SQL operator.

<live-preview>
const search = {
    name: 'John',
    age: 27,
    city: 'Austin',
}
db.all($.from(Contact).where({ equals: search }))
db.all($.from(Contact).where({ notEquals: search }))
db.all($.from(Contact).where({ like: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ notLike: { name:'John', city:'Austin' } }))
db.all($.from(Contact).where({ op: ['>=', { id:10, age:18 }] }))
</live-preview>

### LIKE convenience options

The `startsWith`, `endsWith` and `contains` options can be used to create **LIKE** conditions that match the start, 
end or any part of a string.

<live-preview>
$.from(Contact).where({ 
    startsWith: { city:'A' }, 
    contains: { email:'@gmail.' }, 
    endsWith: { name:'J' }, 
})
</live-preview>

### NULL check convenience options

Whilst the `isNull` and `notNull` convenience options can be used to create **IS NULL** and **IS NOT NULL** conditions.

<live-preview>
$.from(Contact).where({ 
    isNull: ['city', 'age'], 
    notNull: ['email'], 
})
</live-preview>

## Reset WHERE

Calling `where` with no arguments will reset the WHERE clause:

<live-preview>
$.from(Contact).where`name LIKE ${'John%'}`.where().and`id = ${1}`
</live-preview>


# GROUP BY Examples
Source: https://razor-press.web-templates.io/group-by

## Simple GROUP BY

`groupBy` works like Query Builder methods where it's called with the query's table references in the order they were added: 

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, SUM(${o.total}) AS Total`)
</live-preview>

## Multiple GROUP BY

Multiple group by's can be added in one or multiple `groupBy` methods:

<live-preview>
const q = $.from(Contact,'c')
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, SUM(${o.total})`)
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## GROUP BY Builder

When more flexibility is needed, `$.groupBy` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .join(OrderItem, { as:'i', on:(o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.id} = ${i.orderId}` })
    .groupBy(
        $.groupBy(Contact,OrderItem)
            .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
            .add((_,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${i.sku}`)
    )
    .select((c,o,i) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${i.sku}, SUM(${o.total}) AS total`)
</live-preview>

## Reset GROUP BY

Calling `groupBy` with no arguments will reset the GROUP BY clause:

<live-preview>
$.from(Contact).groupBy`name`.groupBy().select`name`
</live-preview>


# HAVING Examples
Source: https://razor-press.web-templates.io/having

## Simple HAVING

The `having` method can be used to filter the results of a `groupBy` query:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Multiple HAVING

Multiple having's can be added in one or multiple `having` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
    .select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
    
db.all(q.clone().having((c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5 AND SUM(${o.total}) < 1000`))
db.all(q.clone()
    .having(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`).having((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`))
</live-preview>

## HAVING Builder

When more flexibility is needed, `$.having` can be used to create a HAVING builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact)
.join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
.groupBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`)
.having(
    $.having(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  COUNT(${c.id}) > 5`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  SUM(${o.total}) < 1000`)
).select(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}, COUNT(${c.id})`)
</live-preview>

## Reset HAVING

Calling `having` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).having`name`.having().select`name`
</live-preview>


# ORDER BY Examples
Source: https://razor-press.web-templates.io/order-by

## Simple ORDER BY

Like other Query Builder methods, `orderBy` is called with the query's table references in the order they were added:

<live-preview>
$.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
</live-preview>

## Multiple ORDER BY

Multiple order by's can be added in one or multiple `orderBy` methods:

<live-preview>
const q = $.from(Contact)
    .join(Order, { on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}, ${o.total}`)
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${c.city}`))
db.all(q.clone().orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`).orderBy(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.city}`))
</live-preview>

## ORDER BY Builder

When more flexibility is needed, `$.orderBy` can be used to create a ORDER BY builder which can be constructed independently
of the query:

<live-preview>
$.from(Contact,'c')
    .join(Order, { as:'o', on:(c,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.id} = ${o.contactId}` })
    .select((c, o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}, ${o.total}`)
    .orderBy(
        $.orderBy(Contact,Order)
        .add(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.name}`)
        .add((_,o) => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${o.total} DESC`)
    )
</live-preview>

## Reset ORDER BY

Calling `orderBy` with no arguments will reset the ORDER BY clause:

<live-preview>
$.from(Contact).orderBy`name`.orderBy().select`name`
</live-preview>


# INSERT Examples
Source: https://razor-press.web-templates.io/insert

## Simple INSERT

Insert a new entity into a table using the `db.insert` driver method:

<live-preview>
db.insert(new Contact({ name:'John', email:'john@email.com', age:27 }))
db.insert(new Product({ sku:'WIDGET', name:'Acme Widget', cost:10 }))
</live-preview>

## INSERT Multiple Rows

Insert multiple entities into a table using the `db.insertAll` driver method:

<live-preview>
db.insertAll([
    new Contact({ name:'John', email:'john@email.com', age:27 }),
    new Contact({ name:'Jane', email:'jane@email.com', age:31 })
])
</live-preview>

## INSERT Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { name, age } = { name:'John', age:27 }
db.run`INSERT INTO Contact (name,age) VALUES (${name},${age})`
</live-preview>


# UPDATE Examples
Source: https://razor-press.web-templates.io/update

## Simple UPDATE

Simple updates can be executed directly on the litdb driver APIs which will update all properties of a data model:

<live-preview>
contact = new Contact({ id:1, name:'John', email:'john@mail.org' })
db.update(contact)
</live-preview>

## UPDATE Specific Properties

For updating specific properties of a data model, the `onlyProps` option can be used:

<live-preview>
db.update(new Contact({ id:1, email:'john@mail.org' }), { onlyProps:['email'] })
</live-preview>

## UPDATE Query Builder

When more flexibility is needed you can use `$.update()` to create an UPDATE query builder:

<live-preview>
db.run($.update(Contact).set({ age:41, city:'Austin' }).where($.idEquals(1)))
const { age, city, email } = { age:41, email:'john@mail.org' }
const q = $.update(Contact)
if (age) q.set({ age })
if (city) q.set({ city })
if (email) q.set({ email })
db.run(q.where($.idEquals(1)))
</live-preview>

## UPDATE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const { id, name, age } = { id:1, name:'John', age:27 }
db.run`UPDATE Contact SET name=${name}, age=${age} WHERE id=${id}`
</live-preview>


# DELETE Examples
Source: https://razor-press.web-templates.io/delete

## Simple DELETE

Delete an entity using the `db.delete` driver method:

<live-preview>
db.delete(new Contact({ id:1, name:'John', email:'john@email.com', age:27 }))
</live-preview>

## DELETE Query Builder
    
When a custom query is needed you can use `$.deleteFrom()` to create a DELETE query builder:

<live-preview>
const yearAgo = new Date(new Date().setFullYear(new Date().getFullYear() - 1))
db.run($.deleteFrom(Order).where(c => 


  
  
  
  
  litdb.dev | Top Sites | DialtoneApp
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  
  


  ${c.createdAt} < ${yearAgo}`))
</live-preview>

## DELETE Expression

When the full flexibility of SQL is needed, you can execute a SQL fragment directly:

<live-preview>
const name = 'John'
db.run`DELETE FROM Contact WHERE name = ${name}`
</live-preview>


${c.createdAt} \u003c ${yearAgo}`))\n\u003c/live-preview\u003e\n\n## DELETE Expression\n\nWhen the full flexibility of SQL is needed, you can execute a SQL fragment directly:\n\n\u003clive-preview\u003e\nconst name = 'John'\ndb.run`DELETE FROM Contact WHERE name = ${name}`\n\u003c/live-preview\u003e\n\n\n","robots_txt_url":null,"llms_txt_url":"/api/v1/crawler/top-sites/litdb.dev/llms.txt","llms_full_txt_url":"/api/v1/crawler/top-sites/litdb.dev/llms-full.txt"}}