> ## Documentation Index
> Fetch the complete documentation index at: https://bunnynet-cb9733c2-support-migration.mintlify.site/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

<Steps>
  <Step title="Retrieve database credentials">
    You will need an existing database to continue. If you don't have one, [create one](/database/quickstart).

    Navigate to **Dashboard > Edge Platform > Database > \[Select Database] > Access** to find your database URL and generate an access token.

    <Info>
      You should store these as environment variables to keep them secure.
    </Info>
  </Step>

  <Step title="Install @libsql/client">
    Install the libSQL client package:

    <CodeGroup>
      ```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
      ```
    </CodeGroup>
  </Step>

  <Step title="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,
    });
    ```
  </Step>

  <Step title="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:

    <CodeGroup>
      ```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" },
      });
      ```
    </CodeGroup>
  </Step>
</Steps>

## Using with Bunny Edge Scripting

You can connect [Edge Scripts](/scripting) to your database by adding credentials as environment variables directly from the database dashboard.

See [Edge Scripting](/database/connect/scripting) 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.
</Note>

## Using with Magic Containers

You can connect [Magic Container](/magic-containers) apps to your database by adding credentials as environment variables directly from the database dashboard.

See [Magic Containers](/database/connect/magic-containers) 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.
</Note>

## Response

Each query method returns a `Promise<ResultSet>`:

| Property          | Type                  | Description                                                                            |
| ----------------- | --------------------- | -------------------------------------------------------------------------------------- |
| `rows`            | `Array<Row>`          | An array of Row objects containing the row values, empty for write operations          |
| `columns`         | `Array<string>`       | 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:

<CodeGroup>
  ```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" },
  });
  ```
</CodeGroup>

<Info>
  libSQL supports the same named placeholder characters as SQLite — `:`, `@` and
  `$`.
</Info>

## 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.
</Warning>
