Waddler Documentation

repository·main·Indexed 18 days ago

https://github.com/drizzle-team/waddler

A thin, unified SQL client wrapper providing a modern, template-string-based API for various database dialects. It simplifies database communication via a consistent interface, preventing SQL injection through automatic parameterization. Supported drivers include node-postgres, mysql2, and libsql, with dialect support for PostgreSQL, MySQL, SQLite, ClickHouse, DuckDB, and Gel. Features include bulk operations via sql.values(), result streaming with .stream(), and chunking with .chunked().

Tokens
14.8K
Snippets
43
Records
69
Agent score
63%

What's inside Waddler

  1. What is Waddler

    main

    Waddler is a thin SQL client wrapper that provides a modern API inspired by postgresjs. It is based on ES6 Tagged Template Strings, allowing you to interact with databases using a unified sql template tag instead of learning a specific API for every database client.

    Key characteristics:

    • Dialect Agnostic: It unifies communication with any database dialect or driver supported by Drizzle (e.g., PostgreSQL, MySQL, LibSQL).
    • No Mapping/Parsing: It does not perform complex query building or object-relational mapping; it simply facilitates communication via TCP or HTTP-based clients.
    • Security: It prevents SQL injection by automatically converting template literals into parameterized queries (e.g., converting ${10} to $1 and [10]).
  2. Configure environment variables for integration tests

    main

    To run integration tests, you must configure several environment variables in your .env file. Many providers allow you to reuse the same connection string across multiple variables, but specific settings (like connection pooling) must be toggled correctly.

    Neon, Vercel, and Postgres

    • NEON_HTTP_CONNECTION_STRING: Obtain from your Neon Project Dashboard via Connect > Connection string.
    • NEON_SERVERLESS_CONNECTION_STRING: Reuse the Neon HTTP connection string.
    • VERCEL_POOL_CONNECTION_STRING: Reuse the Neon connection string, but ensure Connection pooling is enabled in the connection modal.
    • VERCEL_CLIENT_CONNECTION_STRING: Reuse the Neon connection string, but ensure Connection pooling is disabled in the connection modal.
    • POSTGRES_URL: Reuse the VERCEL_POOL_CONNECTION_STRING value.

    Xata

    • XATA_DATABASE_URL: The HTTP endpoint found in the Settings > Connect to your Database section of your Xata database.
    • XATA_API_KEY: A personal API key generated in your Xata account settings.
    • XATA_BRANCH: The name of your testing branch (defaults to main).

    LibSQL (Turso)

    • LIBSQL_REMOTE_URL: The database URL obtained from the Turso dashboard.
    • LIBSQL_REMOTE_TOKEN: The token generated via the Create Token process in Turso.

    TiDB and PlanetScale

    • TIDB_CONNECTION_STRING: The connection string from the TiDB Cloud Connect modal.
    • PLANETSCALE_CONNECTION_STRING: The connection string from your PlanetScale database.

    Test Execution Control

    • RUN_EXTERNAL_DB_TESTS: Set this to any value (e.g., 1) to enable tests for external drivers (Neon, Vercel, Xata, LibSQL, PlanetScale, TiDB). Omit this variable to skip external database tests.
  3. Initialize Waddler with a driver

    main

    To use Waddler, import the driver-specific entry point and initialize it using the waddler function. You can provide a dbUrl via an options object or use default configuration.

    Supported driver entry points include:

    • waddler/node-postgres
    • waddler/mysql2
    • waddler/libsql
    import { waddler } from "waddler/node-postgres";
    
    const sql = waddler({ dbUrl: process.env.DB_URL });
    // or
    const sql = waddler();
  4. Use Waddler with different database drivers

    main

    Waddler provides a unified interface for various TypeScript-compatible database drivers. Instead of a single entry point, you import the waddler function from a driver-specific subpath. Supported drivers include node-postgres, mysql2, and libsql.

    import { waddler } from "waddler/node-postgres";
    import { waddler } from "waddler/mysql2";
    import { waddler } from "waddler/libsql";
    
    const sql = waddler({ dbUrl: process.env.DB_URL });
  5. Initialize a Cloudflare D1 driver with waddler()

    main

    To use Waddler with a Cloudflare D1 database, use the waddler function. This function accepts a client (the D1 database instance) and an optional config object of type WaddlerConfig. It returns a D1SQL instance which provides the SQL template tag and helper functions for executing queries.

    import { waddler } from 'waddler/sqlite/d1';
    
    // Assuming 'env.DB' is your Cloudflare D1 database instance
    const sql = waddler({ client: env.DB });
    
    // Usage example:
    const results = await sql`SELECT * FROM users WHERE id = ${1}`;
  6. Initialize Waddler with Better-SQLite3

    main

    The waddler function is the primary entrypoint for initializing a Waddler instance using the better-sqlite3 driver. It supports several initialization patterns depending on whether you provide a connection string, a configuration object, or an existing better-sqlite3 client.

    Initialization Patterns

    1. Default (In-memory): waddler() creates an in-memory database.

    2. Connection String: waddler('path/to/db.sqlite') connects to a specific file.

    3. Configuration Object: Pass a WaddlerConfig object containing either a connection or a client.

      • Using connection: Can be a string (path) or an object containing source (string or Buffer) and better-sqlite3 Options.
      • Using client: Pass an existing better-sqlite3 Database instance.

    Logger Configuration

    Within the configuration object, you can control logging via the logger key:

    • logger: true: Uses the DefaultLogger.
    • logger: false: Disables logging.
    • logger: <LoggerInstance>: Uses a custom logger implementation.
    import { waddler } from 'waddler';
    
    // 1. Simple connection string
    const sql = waddler('my-database.db');
    
    // 2. Advanced configuration with custom logger and connection options
    const sql = waddler({
      logger: true,
      connection: {
        source: 'my-database.db',
        // other better-sqlite3 Options here
      }
    });
    
    // 3. Using an existing better-sqlite3 client
    import Client from 'better-sqlite3';
    const client = new Client('my-database.db');
    const sql = waddler({
      client,
      logger: false
    });
  7. Initialize Waddler with Bun-SQLite

    main

    To use Waddler with a Bun-SQLite database, call the waddler function. You can initialize it in several ways:

    1. In-memory database: Call waddler() with no arguments.
    2. File-based database: Call waddler('path/to/db.sqlite').
    3. Using an existing Bun Database client: Pass a configuration object containing a client property.
    4. Using a connection configuration: Pass a configuration object containing a connection property, which can be a string (path) or an object specifying source and database options.

    You can also pass a WaddlerConfig object to enable logging.

    import { waddler } from 'waddler/sqlite/bun-sqlite';
    
    // 1. In-memory
    const sql = waddler();
    
    // 2. File-based
    const sql = waddler('my-db.sqlite');
    
    // 3. Existing Bun client
    import { Database } from 'bun:sqlite';
    const client = new Database('my-db.sqlite');
    const sql = waddler({ client });
    
    // 4. Connection object
    const sql = waddler({
      connection: {
        source: 'my-db.sqlite',
        readonly: true
      },
      logger: true
    });
  8. Initialize the Durable-SQLite driver with waddler()

    main

    To use Waddler with a Durable Object storage client, call the waddler function. This returns a DurableSqliteSQL template function used to construct SQL queries. You can optionally provide a WaddlerConfig object to configure logging behavior.

    Configuration

    • config.logger:
      • true: Enables the DefaultLogger.
      • false: Disables logging.
      • Logger instance: Provides a custom logger implementation.
    import { waddler } from 'waddler';
    
    const sql = waddler({
      client: myDurableObjectStorage,
      config: { logger: true }
    });
  9. Execute queries using the sql template tag

    main

    Waddler uses tagged template strings to execute queries. The API is promisified, allowing you to await the result of the sql tag directly.

    Basic Query

    const result = await sql`select * from users`;

    Parameterized Queries (SQL Injection Protection)

    Pass variables directly into the template string. Waddler handles the parameterization automatically.

    await sql`select * from users where id = ${10}`;

    Typed Queries

    You can pass a generic type to the sql tag to define the shape of the returned rows.

    await sql<{ id: number, name: string }>`select * from users`;
    const result = await sql`select * from users`;
    
    // with types
    await sql<{ id: number, name: string }>`select * from users`;
  10. Stream and chunk query results

    main

    Waddler supports asynchronous iteration over query results through .stream() and .chunked(size) methods. .stream() allows you to iterate over individual rows, while .chunked(n) allows you to iterate over batches of n rows.

    // Streaming individual rows
    const stream = sql`select * from users`.stream();
    for await (const row of stream) {
      console.log(row);
    }
    
    // Chunking results into batches of 2
    const chunked = sql`select * from users`.chunked(2);
    for await (const chunk of chunked) {
      console.log(chunk);
    }
  11. Stream and chunk query results

    main

    Waddler provides built-in support for processing large result sets through streaming and chunking.

    Streaming

    Use .stream() to get an async iterator that yields rows one by one.

    const stream = sql`select * from users`.stream();
    for await (const row of stream) {
      console.log(row);
    }

    Chunking

    Use .chunked(size) to get an async iterator that yields arrays of rows of a specified size.

    const chunked = sql`select * from users`.chunked(2);
    for await (const chunk of chunked) {
      console.log(chunk);
    }
    const stream = sql`select * from users`.stream();
    for await (const row of stream) {
      console.log(row);
    }
    
    const chunked = sql`select * from users`.chunked(2);
    for await (const chunk of chunked) {
      console.log(chunk);
    }
  12. Manage query values with sql.values()

    main

    The sql.values() method allows you to prepare arrays of data for bulk operations or complex inserts, ensuring they are correctly formatted for the template tag.

    const values = sql.values([["Dan", "dan@acme.com", 25]]);
    await sql`insert into "users" ("name", "email", "age") values ${values}`;
    // Resulting SQL: insert into "users" ("name", "email", "age") values ('Dan', 'dan@acme.com', 25);