# Connecting from code

> Query a Resta database with the Turso JavaScript client or plain HTTP, including transactions, batches and streaming results.

Source: https://resta.dev/docs/clients · Updated 2026-11-01

**Connect to a Resta database with its URL and a database token, using the official `@tursodatabase/serverless` client or any HTTP client.** Resta speaks Turso's "SQL over HTTP" protocol, version 3, so code written for Turso's serverless driver works by changing the URL and token.

## JavaScript and TypeScript

`@tursodatabase/serverless` 1.4.1 is tested against Resta: queries, streaming results, interactive and atomic transactions, batches, errors and read-only tokens.

```sh
npm install @tursodatabase/serverless
```

```js
import { connect } from '@tursodatabase/serverless';

const db = connect({
  url: 'https://db.example.com/db/user-42-9cbfaa9b',
  authToken: process.env.RESTA_DB_TOKEN,
});

// Statements
await db.exec('CREATE TABLE IF NOT EXISTS todos (id INTEGER PRIMARY KEY, title TEXT, done INTEGER DEFAULT 0)');
const { lastInsertRowid } = await db.run('INSERT INTO todos (title) VALUES (?)', 'write docs');
const open = await db.all('SELECT * FROM todos WHERE done = 0');

// Named parameters
await db.run('UPDATE todos SET done = 1 WHERE id = :id', { id: lastInsertRowid });

// Interactive transaction: rolled back if the function throws
await db.transactionAsync(async (tx) => {
  await tx.run('INSERT INTO todos (title) VALUES (?)', 'a');
  await tx.run('INSERT INTO todos (title) VALUES (?)', 'b');
})();

// Atomic batch in one request: all statements or none
await db.batch(
  [
    { sql: 'INSERT INTO todos (title) VALUES (?)', args: ['c'] },
    'DELETE FROM todos WHERE done = 1',
  ],
  'immediate',
);

// Stream a large result row by row
for await (const row of (await db.prepare('SELECT * FROM todos')).iterate()) {
  console.log(row.title);
}
```

## Plain HTTP

Any language can send the protocol directly. One request, one statement:

```sh
curl https://db.example.com/db/user-42-9cbfaa9b/v3/pipeline \
  -H "Authorization: Bearer $RESTA_DB_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"requests": [
        {"type": "execute", "stmt": {"sql": "SELECT * FROM todos WHERE id = ?", "args": [{"type": "integer", "value": "1"}]}},
        {"type": "close"}
      ]}'
```

Values are tagged: `{"type": "integer", "value": "1"}` (integers as strings), `{"type": "float", "value": 1.5}`, `{"type": "text", "value": "hi"}`, `{"type": "blob", "base64": "..."}`, `{"type": "null"}`.

| Endpoint | Use |
|---|---|
| `POST /db/{id}/v3/pipeline` | Run requests in order on a stream. |
| `POST /db/{id}/v3/cursor` | Run a batch and stream rows back, one JSON object per line. |
| `POST /db/{id}/v2/pipeline` | Older clients. |

Each response includes `rows_read`, `rows_written` and `query_duration_ms` per statement. SQL errors come back inside a `200` response with a code such as `SQLITE_CONSTRAINT`; HTTP errors (`401`, `404`, `503`) mean the request itself failed.

## Transactions over HTTP

Send `BEGIN` in one request. The response has a `baton`; send it with the next request to continue the same transaction, and finish with `COMMIT` or `ROLLBACK`. A baton is single-use and expires after 30 seconds idle, which rolls the transaction back.

## Other languages

Clients for Turso's protocol in other languages should work, but only the JavaScript client is tested against Resta so far.

## Choosing tokens

- **Read-only** (`"access": "ro"`) wherever writes are not needed.
- **One database per token**, from `POST /v1/databases/{name}/tokens`, not the server-wide token.
- **Short lifetimes**, minted by your backend when needed. Tokens cannot be revoked individually.
