# ?uery ?uery is a SQL library for Bun that helps glue your database and class objects together with little setup and minimal fuss. The API is simple and fully typed. Don't hide SQL - embrace it. Currently supports both SQLite and PlanetScale. Best used for prototypes and all kinds of spikes. ## SQL You can use SQL, but data mapping (next section) is the best way to use this library. ```typescript import { openDB } from "query" const db = await openDB({ sqlite: "users.db" }) await db.run( "CREATE TABLE IF NOT EXISTS users (ID INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)" ) await db.run("INSERT INTO users (name) VALUES ($name)", { name: "John" }) const user = await db.queryOne("SELECT * FROM users WHERE name = $name", { name: "John", }) console.log("Name:", user["name"]) ``` ## Data Mapping It's not an ORM. Think of it more like SQL queries as typed templates, automatically created based on your class objects. ```typescript import { openDB } from "query" // Define your model - just needs an id property class User { id: number name: string } const db = await openDB({ sqlite: "users.db" }).model(User, "users") // connect your model to a db table <-- this is where the magic happens await db.run( "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT)" ) const user = await db.users.create({ name: "John" }) console.log(user.name) // "John" const john = await db.users.find(1) // or await db.users.one({name: "John"}) const updated = await db.users.update(1, { name: "John Smith" }) const smiths = await db.users.all("name LIKE $name", { name: "Smith%" }) await db.users.delete(1) // SQL is encouraged const results = await db.query( "SELECT * FROM users WHERE name LIKE $search", { search: "J%" } ) ``` ## `async` API Everything needs `await`. Everything. ### Queries The main way you'll query the DB is with `db.table.one()` and `db.table.all()`. Each takes either: 1. SQL String + Args 2. Query Builder ```typescript // SQL Args const everyoneNamedBob = db.users.all("name = $name LIMIT 2", { name: "Bob" }) // Query Builder const everyoneNamedJon = db.users.all({ name: "Jon", age: 20, limit: 2 }) ``` The Query Builder has three special keys: - limit - offset - order ### Database Operations - `openDB(config: { sqlite: string } | { planetscale: PlanetScaleConfig })`: Opens a database connection - `.model(modelClass: Model, tableName: string): DB`: Chain this with `openDB()` to get your `db` objects. - `db.query(sql: string, args?: {}, map?: (row: T) => U): U[]`: Query multiple rows - `db.queryOne(sql: string, args?: {}, map?: (row: T) => U): U | null`: Query single row - `db.run(sql: string, args?: {})`: Execute SQL with no results - `db.transaction(fn)`: Run multiple SQL statements together, rolling them all back if an error is thrown. - `db.close()`: Close connection ### Mapper API - `db.table.find(id: string | number): Model | null`: Find a record by ID - `db.table.create(data: Partial): Model`: Create a new record - `db.table.update(id: string | number, data: Partial): Model | null`: Update a record - `db.table.delete(id: string | number): boolean`: Delete a record - `db.table.one(where?: string, args?: Record): Model | null`: Get one record with optional where clause - `db.table.all(where?: string | Record, args?: Record): Model[]`: Get all records with optional where clause or object-based query - `db.table.query(sql: string, args?: {}): Model[]`: Query multiple rows - `db.table.queryOne(sql: string, args?: {}): Model | null`: Query single row ### Database Operations ```typescript // Basic CRUD const user = await db.users.find(1) const users = await db.users.all() const filtered = await db.users.all("age > $age", { age: 18 }) const byName = await db.users.all({ name: "John" }) // Object-based query const oneUser = await db.users.one("email = $email", { email: "test@example.com", }) // Transactions await db.transaction(async (tx) => { const user = await db.users.create({ name: "Alice" }) const post = await db.posts.create({ userId: user.id, title: "First Post" }) }) ```