Deno SQLite3

repository·main·Indexed 43 days ago

https://github.com/denodrivers/sqlite3

A high-performance SQLite3 module for Deno that utilizes the FFI API for native speed, providing a faster alternative to WASM-based implementations. It features a Database class for connection management, support for prepared statements, transaction handling with SAVEPOINT for nested transactions, and automatic JSON parsing.

Tokens
10K
Snippets
33
Records
44
Agent score
55%

What's inside Deno SQLite3

  1. Quickstart with Deno SQLite3

    main

    To use Deno SQLite3, import the Database class from the JSR registry. You can initialize a new database by providing a filename. The library allows you to prepare statements and retrieve values using the .value<T>() method on the prepared statement.

    import { Database } from "jsr:@db/sqlite@0.13";
    
    const db = new Database("test.db");
    
    const [version] = db.prepare("select sqlite_version()").value<[string]>()!;
    console.log(version);
    
    db.close();
  2. Handle BLOB data with Uint8Array

    main

    The library uses Uint8Array for BLOB types to ensure V8 Fast API optimization and consistency. To work with other typed arrays (like Float32Array), you should convert them using the underlying buffer to avoid unnecessary copies.

    const f32 = new Float32Array(1);
    const u8 = new Uint8Array(f32.buffer); // no copy, can pass this
    
    const u8FromSqlite = new Uint8Array(4);
    const f32FromSqlite = new Float32Array(u8FromSqlite.buffer); // safely convert back when retrieved from sqlite, no copy
  3. Configure Deno permissions for SQLite3

    main

    Because this library relies on the Deno FFI (Foreign Function Interface) API, you must grant specific permissions when running your script. You need --allow-env and --allow-ffi to support FFI and the automatic downloading/caching of the prebuilt native library. Additionally, network and file system permissions are required for the initial download and caching process.

    For convenience, you can use the --allow-all or -A flag to grant all permissions.

    deno run -A <file>
  4. Open a SQLite database connection

    main

    To open a database connection, instantiate the Database class with a file path or a special string for in-memory databases. If the file does not exist, it will be created by default unless create: false is specified in the options.

    // Open using default options
    const db = new Database("test.db");
    
    // Open using URL path (relative to current file/module, not CWD)
    const db = new Database(new URL("./test.db", import.meta.url));
    
    // Open in memory
    const db = new Database(":memory:");
    
    // Open in read-only mode
    const db = new Database("test.db", { readonly: true });
    
    // Open existing database, error if it doesn't exist
    const db = new Database("test.db", { create: false });
  5. Manage transactions with `transaction()`

    main

    The transaction(fn) method wraps a callback function in a transaction.

    • If the function returns successfully, the transaction is committed.
    • If the function throws an error, the transaction is rolled back.

    If a transaction is already active (e.g., nested calls), the library automatically uses SAVEPOINT and RELEASE instead of BEGIN/COMMIT to support nested transactions.

    You can specify the transaction type using the following properties on the returned function:

    • default: Uses BEGIN.
    • deferred: Uses BEGIN DEFERRED.
    • immediate: Uses BEGIN IMMEDIATE.
    • exclusive: Uses BEGIN EXCLUSIVE.
    const stmt = db.prepare("insert into users (id, username) values(?, ?)");
    
    interface User {
      id: number;
      username: string;
    }
    
    const insertUsers = db.transaction((data: User[]) => {
      for (const user of data) {
        stmt.run(user.id, user.username);
      }
    });
    
    insertUsers([
      { id: 1, username: "alice" },
      { id: 2, username: "bob" },
    ]);
    
    // Or use specific modes:
    insertUsers.deferred([
      { id: 3, username: "charlie" },
    ]);
  6. Configure Database connection options

    main

    When constructing a Database instance, you can pass an options object to configure connection behavior:

    • create: boolean - Whether to create the database file if it does not exist. Defaults to true.
    • readonly: boolean - Whether to open the database in read-only mode. Defaults to false.
    • memory: boolean - Whether to open the database in memory. Defaults to false.
    • int64: boolean - Whether to support BigInt columns. If false (default), integers larger than 32-bit may be inaccurate and return mixed types like number | bigint.
    • flags: number - Raw flags to pass to the C API. Note: passing this ignores all other options.
    • unsafeConcurrency: boolean - Enables optimizations that affect synchronization with other clients (useful for single-client scenarios).
    • enableLoadExtension: boolean - Enables loading SQLite extensions from dynamic libraries. Must be true to use loadExtension(). Defaults to false.
    • parseJson: boolean - Enables automatic parsing of JSON values. Defaults to true.
  7. Use a custom SQLite3 shared library

    main
    By default, the library downloads and caches a prebuilt shared library from GitHub releases. If you need to use a specific or custom SQLite3 shared library, set the DENO_SQLITE_PATH environment variable to the absolute path of your library.
  8. Initialize a Database connection

    main

    Use the Database class to open a connection to a SQLite database. You can open a database using a file path string, a URL, or an in-memory database.

    Common configuration options include:

    • readonly: If true, the database is opened in read-only mode.
    • create: If true (default), a new database file is created if it doesn't exist. Note: this is ignored if readonly is true.
    • memory: If true, opens an in-memory database.
    • int64: If true, enables support for BigInt columns. If false (default), integers larger than 32-bit may be inaccurate.
    • parseJson: If true (default), JSON columns are parsed as JavaScript objects.
    • enableLoadExtension: If true, allows loading SQLite extensions via loadExtension().
    // Open a database from file, creates if doesn't exist.
    const db = new Database("myfile.db");
    
    // Open an in-memory database.
    const db = new Database(":memory:");
    
    // Open a read-only database.
    const db = new Database("myfile.db", { readonly: true });
    
    // Or open using File URL
    const db = new Database(new URL("./myfile.db", import.meta.url));
  9. Manage Statement lifecycle and memory

    main

    Prepared statements consume resources in the SQLite engine. To prevent memory leaks, you should explicitly release them.

    • Manual: Call stmt.finalize() to free the statement object.
    • Automatic (Disposable): The Statement class implements [Symbol.dispose], so you can use it within a using block (in supported TypeScript/Deno environments).
    • Garbage Collection: The driver uses a FinalizationRegistry to attempt to finalize statements when they are garbage collected, but explicit finalization is preferred.
    // Using explicit finalization
    const stmt = db.prepare("SELECT 1");
    stmt.finalize();
    
    // Using Symbol.dispose (if supported)
    { 
      using stmt = db.prepare("SELECT 1");
      stmt.run();
    }
  10. Execute SQL statements with exec()

    main

    Use db.exec(sql, ...params) to execute SQL.

    • If no parameters are provided, it executes all statements in the string and returns the number of changes made by the last statement. This is ideal for DDL (CREATE, DROP, etc.) or PRAGMA statements.
    • If parameters are provided, it uses the prepared statement API and can only execute one statement at a time.

    Alternatively, use the .sql tagged template for safe, parameterized queries that return all rows via .all().

    // Executing multiple statements (DDL/Pragma)
    const changes = db.exec(
      "CREATE TABLE foo (bar TEXT); INSERT INTO foo VALUES ('baz');",
    );
    
    // Using tagged template for safe parameterized queries
    const minimum = 20;
    const results = db.sql`
      SELECT id, name, age FROM students WHERE age > ${minimum}`;