Bob a new way to write SQL
Bob is under development! Use at your own risk.

@bob-kit/client

Official Bob client for JavaScript/TypeScript
Official Bob client for consuming the bob DSL from JavaScript/TypeScript.

This package loads Bob's runtime (WASM) and exposes a small API to:
  • Transpile your bob input into SQL for a given engine (SQLite, MariaDB/MySQL, PostgreSQL).
  • Automatically execute the generated SQL using a Driver you provide.
  • Optionally cache transpilation results.
Bob is a lightweight, declarative transpiler that lets you define database schemas and queries using a simple, human-readable DSL called bob.

Bob then converts those definitions into SQL for multiple engines such as SQLite, MariaDB/MySQL, and PostgreSQL.

Instead of writing verbose SQL by hand, you describe tables, relationships, and queries concisely, and Bob generates the corresponding SQL.

Installation

pnpm

pnpm add @bob-kit/client

npm

npm i @bob-kit/client

yarn

yarn add @bob-kit/client

Environment Requirements

Right now, @bob-kit/client loads the WASM using Node APIs (fs, vm).

That means it's currently Node.js-oriented (not browser) as implemented today.

Concepts: Driver and Cache

The client needs a Driver (from @bob-kit/types) to execute the generated SQL:
  • driver: target engine ("sqlite" | "mariadb" | "mysql" | "postgres").
  • querier(query, ...params): async function that executes SQL (and returns rows for SELECT).
  • cache: optional implementation to cache transpilation results (memory, redis, etc.).

This package also exports a ready-to-use in-memory cache: MemoryCache.

Optional Drivers

Drivers are optional packages that you can install separately from the Bob ecosystem. They provide ready-to-use querier implementations for different database engines.
  • @bob-kit/sqlite: Official SQLite driver. Install with pnpm add @bob-kit/sqlite, npm i @bob-kit/sqlite, or yarn add @bob-kit/sqlite.
  • PostgreSQL, MariaDB, and MySQL: Official drivers are pending but coming soon. In the meantime, you're free to create your own driver libraries as long as they respect the querier contract required by Bob.

Custom Drivers: You can implement your own driver by creating a function that matches the querier interface: (query: string, ...params: any[]) => Promise<any[]>. This allows you to integrate Bob with any database or ORM that supports parameterized queries.

Quick Start (Node + SQLite)

Complete example using @bob-kit/sqlite as the querier:
import bob from "@bob-kit/client";
import { MemoryCache } from "@bob-kit/client/cache";
import { sqlite } from "@bob-kit/sqlite";

const querier = sqlite(":memory:");

const client = bob({
  driver: "sqlite",
  querier,
  cache: new MemoryCache(),
});

// 1) Define multiple tables in a single query
await client.query`
  table Profile {
    id id
    avatar string
  }

  table Users {
    id id
    name string
    lastName string
    Profile
  }
`;

// 2) Insert one entity
await client.query`
  new Profile {
    avatar "bear"
  }
`;

// 3) Bulk insert with multiple rows and columns
await client.query`
  new Users name          lastName      Profile.id {
            "John"        "Doe"         1
            "Mary"        "Jane"        3
            "Robert"      "Smith"       3
  }
`;

// 4) Create reusable query functions with types
const getUserByName = (name: string) => client.query`
  get Users {
    name
    profile_id

    -> Profile {
        avatar
    }

    desc Profile.id
  }
`;

const users = await getUserByName("John");

Advanced Examples

Joins and Relationships

Join with related table:
const usersWithProfiles = await client.query`
  get Users {
    id
    name
    lastName

    -> Profile {
      avatar
    }
  }
`;

Filtering and Sorting

Filter and sort your queries:
const filteredUsers = await client.query`
  get Users {
    id
    name
    if name = "John" || "Mary"
  }
`;

const sortedUsers = await client.query`
  get Users {
    id
    name
    desc id
  }
`;

Parameters

The API accepts parameters as arguments to query(...). Internally, the template literal is converted to a string using ? as the placeholder.
const userEmail = "ada@lovelace.dev"

const byEmail = await client.query`
  get Users {
    id
    name
    email
    if email == ${userEmail}
  }
`;

API Reference

default export function bob(driver: Driver)

Creates a client instance.

Returns an object with:
  • query<A>(input, ...params): Promise<A[]>: executes a bob input. You can destructure it for convenience: const { query: b } = bob(...).
  • cache(id)(input, ...params): Promise<A[]>: same as query, but caches the transpilation result under a fixed id.
  • factory.<id>(input, ...params): Promise<A[]>: shortcut for cache("<id>")(...).

bobQuery({ driver, input, id? })

Low-level function that only performs transpilation (and applies cache if present). You usually won't need it.

Driver Interface

The Driver interface (from @bob-kit/types) defines the contract that all drivers must implement:
  • driver: string indicating the target engine ("sqlite" | "mariadb" | "mysql" | "postgres").
  • querier(query: string, ...params: any[]): Promise<any[]>: async function that executes SQL queries. For SELECT queries, it should return an array of rows. For other queries (INSERT, UPDATE, DELETE), it can return an empty array or the result of the operation.
  • cache: optional cache implementation to store transpilation results (can be MemoryCache, Redis, or any custom implementation).

Note: Drivers are optional packages that you can install separately. The official @bob-kit/sqlite driver is available now, while PostgreSQL, MariaDB, and MySQL drivers are pending. You can create your own driver libraries as long as they respect this interface contract.

@bob-kit/client/cache

Includes:
  • [object Object]

Errors

When Bob returns a parsing/validation/transpilation error, the client throws BobError with:
  • name a value from BobErrors (from @bob-kit/types/errors)
  • message error details

Important Notes

  • Multiple actions: if your input generates more than 1 SQL action, the client throws (MultipleActions). The current flow assumes 0 or 1 action per execution.
  • Table execution: if the output includes tables, all of them are executed (in order) before the action/query.