> ## Documentation Index > Fetch the complete documentation index at: https://bunny.net/docs/llms.txt > Use this file to discover all available pages before exploring further. # TypeScript > Get started with Bunny Database and TypeScript using the libSQL client In this TypeScript quickstart you will learn how to: * Retrieve database credentials * Install the libSQL client * Connect to a remote Bunny Database * Execute a query using SQL ## Quickstart **1. Retrieve database credentials** You will need an existing database to continue. If you don't have one, [create one](https://bunny.net/docs/database/quickstart.md). Navigate to **Dashboard > Edge Platform > Database > \[Select Database] > Access** to find your database URL and generate an access token. > [!NOTE] > > You should store these as environment variables to keep them secure. **2. Install @libsql/client** Install the libSQL client package: ```bash npm theme={null} npm install @libsql/client ``` ```bash pnpm theme={null} pnpm add @libsql/client ``` ```bash yarn theme={null} yarn add @libsql/client ``` ```bash bun theme={null} bun add @libsql/client ``` **3. Initialize a new client** Create a client instance with your database URL and auth token: ```typescript theme={null} import { createClient } from "@libsql/client/web"; const client = createClient({ url: process.env.BUNNY_DATABASE_URL, authToken: process.env.BUNNY_DATABASE_AUTH_TOKEN, }); ``` **4. Execute a query using SQL** You can execute a SQL query against your database by calling `execute()`: ```typescript theme={null} await client.execute("SELECT * FROM users"); ``` If you need to use placeholders for values, you can do that: ```typescript Positional theme={null} await client.execute({ sql: "SELECT * FROM users WHERE id = ?", args: [1], }); ``` ```typescript Named theme={null} await client.execute({ sql: "INSERT INTO users VALUES (:name)", args: { name: "Kit" }, }); ``` ## Using with Bunny Edge Scripting You can connect [Edge Scripts](https://bunny.net/docs/scripting.md) to your database by adding credentials as environment variables directly from the database dashboard. See [Edge Scripting](https://bunny.net/docs/database/connect/scripting.md) for step-by-step instructions on connecting your script to Bunny Database. ```typescript theme={null} import { createClient } from "@libsql/client/web"; const client = createClient({ url: process.env.BUNNY_DATABASE_URL, authToken: process.env.BUNNY_DATABASE_AUTH_TOKEN, }); const result = await client.execute("SELECT * FROM users"); ``` > [!NOTE] > > When using Edge Scripting, your database credentials are automatically > available as `BUNNY_DATABASE_URL` and `BUNNY_DATABASE_AUTH_TOKEN` environment variables after generating a > token from the database dashboard. ## Using with Magic Containers You can connect [Magic Container](https://bunny.net/docs/magic-containers.md) apps to your database by adding credentials as environment variables directly from the database dashboard. See [Magic Containers](https://bunny.net/docs/database/connect/magic-containers.md) for step-by-step instructions on connecting your app to Bunny Database. ```typescript theme={null} import { createClient } from "@libsql/client/web"; const client = createClient({ url: process.env.BUNNY_DATABASE_URL, authToken: process.env.BUNNY_DATABASE_AUTH_TOKEN, }); const result = await client.execute("SELECT * FROM users"); ``` > [!NOTE] > > When using Magic Containers, your database credentials are automatically > available as `BUNNY_DATABASE_URL` and `BUNNY_DATABASE_AUTH_TOKEN` environment variables after generating a > token from the database dashboard. ## Response Each query method returns a `Promise`: | Property | Type | Description | | - | - | - | | `rows` | `Array` | An array of Row objects containing the row values, empty for write operations | | `columns` | `Array` | An array of strings with the names of the columns in the order they appear in each Row | | `rowsAffected` | `number` | The number of rows affected by a write statement, `0` otherwise | | `lastInsertRowid` | `bigint \| undefined` | The ID of a newly inserted row, or `undefined` if there is none for the statement | ## Placeholders libSQL supports the use of positional and named placeholders within SQL statements: ```typescript Positional theme={null} const result = await client.execute({ sql: "SELECT * FROM users WHERE id = ?", args: [1], }); ``` ```typescript Named theme={null} const result = await client.execute({ sql: "INSERT INTO users VALUES (:name)", args: { name: "Kit" }, }); ``` > [!NOTE] > > libSQL supports the same named placeholder characters as SQLite — `:`, `@` and > `$`. ## Batch Transactions A batch consists of multiple SQL statements executed sequentially within an implicit transaction. The backend handles the transaction: success commits all changes, while any failure results in a full rollback with no modifications. ```typescript theme={null} const result = await client.batch( [ { sql: "INSERT INTO users VALUES (?)", args: ["Kit"], }, { sql: "INSERT INTO users VALUES (?)", args: ["Sam"], }, ], "write", ); ``` ### Transaction Modes | Mode | SQLite command | Description | | - | - | - | | `write` | `BEGIN IMMEDIATE` | The transaction may execute statements that read and write data. Write transactions executed on a replica are forwarded to the primary instance, and can't operate in parallel. | | `read` | `BEGIN TRANSACTION READONLY` | The transaction may only execute statements that read data (select). Read transactions can occur on replicas, and can operate in parallel with other read transactions. | | `deferred` | `BEGIN DEFERRED` | The transaction starts in read mode, then changes to write as soon as a write statement is executed. This mode change may fail if there is a write transaction currently executing on the primary. | ## Interactive Transactions Interactive transactions in SQLite ensure the consistency of a series of read and write operations within a transaction's scope. These transactions give you control over when to commit or roll back changes, isolating them from other client activity. | Method | Description | | - | - | | `execute()` | Similar to `execute()` except within the context of the transaction | | `commit()` | Commits all write statements in the transaction | | `rollback()` | Rolls back the entire transaction | | `close()` | Immediately stops the transaction | ```typescript theme={null} const transaction = await client.transaction("write"); try { // Read the current balance const result = await transaction.execute({ sql: "SELECT balance FROM accounts WHERE id = ?", args: [1], }); const currentBalance = result.rows[0].balance as number; const newBalance = currentBalance - 100; // Validate and update based on the read value if (newBalance < 0) { throw new Error("Insufficient funds"); } await transaction.execute({ sql: "UPDATE accounts SET balance = ? WHERE id = ?", args: [newBalance, 1], }); await transaction.commit(); } catch (e) { await transaction.rollback(); } ``` > [!WARNING] > > Interactive transactions in libSQL lock the database for writing until > committed or rolled back, with a 5-second timeout. They can impact performance > on high-latency or busy databases. This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.