> ## 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](/docs/database/quickstart).

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](/docs/scripting) to your database by adding credentials as environment variables directly from the database dashboard.

See [Edge Scripting](/docs/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.

## Using with Magic Containers

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

See [Magic Containers](/docs/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.

## 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:

```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.