> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.recursiv.io/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.recursiv.io/_mcp/server.

# Databases

## Overview

Every project can have managed Postgres databases. The SDK provides methods to list, create, and retrieve credentials for databases. The `ensure` method makes database provisioning idempotent -- call it on every deploy and it will only create the database if it does not already exist.

All database methods are available on `r.databases`.

## Methods

| Method                   | Description                                           |
| ------------------------ | ----------------------------------------------------- |
| `list(params)`           | List all databases for a project.                     |
| `create(input)`          | Create a new database.                                |
| `ensure(input)`          | Ensure a database exists (create if not). Idempotent. |
| `getCredentials(params)` | Get connection credentials for a database.            |
| `query(input)`           | Execute a SQL query against a project's database.     |

## List databases

```typescript
const { data: databases } = await r.databases.list({
  project_id: 'proj_123',
});

for (const db of databases) {
  console.log(`${db.name} — ${db.status}`);
}
```

**Parameters:**

| Parameter    | Type     | Required | Description                        |
| ------------ | -------- | -------- | ---------------------------------- |
| `project_id` | `string` | Yes      | The project to list databases for. |

**Returns:** `SingleResponse<Database[]>`

```typescript
interface Database {
  id: string;
  name: string;
  status: string;      // 'provisioning' | 'ready' | 'error'
  project_id: string;
  created_at: string;
}
```

## Create a database

```typescript
const { data: db } = await r.databases.create({
  project_id: 'proj_123',
  name: 'main',
});

console.log(db.id);     // 'db_abc...'
console.log(db.name);   // 'main'
console.log(db.status); // 'provisioning'
```

**Input fields:**

| Field        | Type     | Required | Description                                       |
| ------------ | -------- | -------- | ------------------------------------------------- |
| `project_id` | `string` | Yes      | Project to create the database in.                |
| `name`       | `string` | Yes      | Database name. Must be unique within the project. |

## Ensure a database (idempotent)

The `ensure` method creates the database if it does not exist, or returns the existing one. This is the recommended approach for deployment scripts and CI/CD pipelines.

```typescript
const { data: db } = await r.databases.ensure({
  project_id: 'proj_123',
  name: 'main',
});

console.log(db.name);   // 'main'
console.log(db.status); // 'ready' (if already provisioned)
```

**Input fields:**

| Field        | Type      | Required | Description                                        |
| ------------ | --------- | -------- | -------------------------------------------------- |
| `project_id` | `string`  | Yes      | Project to ensure the database in.                 |
| `name`       | `string?` | No       | Database name. Defaults to `'default'` if omitted. |

> **Note**
>
> `ensure` is idempotent. Calling it multiple times with the same project and name will always return the same database without creating duplicates.

## Get credentials

Retrieve the connection credentials for a database. Credentials are returned as individual fields and a ready-to-use connection string.

```typescript
const { data: creds } = await r.databases.getCredentials({
  project_id: 'proj_123',
  name: 'main',
});

console.log(creds.host);
console.log(creds.port);
console.log(creds.database);
console.log(creds.username);
console.log(creds.password);
console.log(creds.connection_string);
// postgres://user:pass@host:port/database
```

**Parameters:**

| Parameter    | Type     | Required | Description                              |
| ------------ | -------- | -------- | ---------------------------------------- |
| `project_id` | `string` | Yes      | The project that owns the database.      |
| `name`       | `string` | Yes      | The database name (not the database ID). |

> **Warning**
>
> `getCredentials` takes `project_id` and `name` -- not a database ID. This is intentional: the database name is the stable identifier you use in your code.

**Returns:** `SingleResponse<DatabaseCredentials>`

```typescript
interface DatabaseCredentials {
  host: string;
  port: number;
  database: string;
  username: string;
  password: string;
  connection_string: string;
}
```

## Query a database

Execute SQL queries directly through the SDK — no need to manage database connections yourself.

```typescript
// Create a table
await r.databases.query({
  project_id: 'proj_123',
  sql: `CREATE TABLE IF NOT EXISTS orders (
    id SERIAL PRIMARY KEY,
    user_id TEXT NOT NULL,
    amount INTEGER NOT NULL,
    created_at TIMESTAMP DEFAULT NOW()
  )`,
});

// Insert data with parameterized queries (prevents SQL injection)
await r.databases.query({
  project_id: 'proj_123',
  sql: 'INSERT INTO orders (user_id, amount) VALUES ($1, $2)',
  params: ['user_abc', 4999],
});

// Read data
const { data: result } = await r.databases.query({
  project_id: 'proj_123',
  sql: 'SELECT * FROM orders WHERE user_id = $1 ORDER BY created_at DESC',
  params: ['user_abc'],
});

console.log(result.columns); // ['id', 'user_id', 'amount', 'created_at']
console.log(result.rows);    // [{ id: 1, user_id: 'user_abc', amount: 4999, ... }]
console.log(result.rowCount); // 1
```

**Parameters:**

| Parameter       | Type        | Required | Description                                        |
| --------------- | ----------- | -------- | -------------------------------------------------- |
| `project_id`    | `string`    | Yes      | The project that owns the database.                |
| `sql`           | `string`    | Yes      | SQL query to execute (max 10,000 chars).           |
| `params`        | `unknown[]` | No       | Parameterized query values (use `$1`, `$2`, etc.). |
| `name`          | `string`    | No       | Database name (defaults to `'default'`).           |
| `database_name` | `string`    | No       | **Deprecated.** Alias for `name`.                  |

**Returns:** `SingleResponse<QueryResult>`

```typescript
interface QueryResult {
  columns: string[];
  rows: Record<string, unknown>[];
  rowCount: number;
}
```

> **Note**
>
> Always use parameterized queries (`$1`, `$2`) instead of string interpolation to prevent SQL injection.

## Full example: provision and migrate

This example shows a complete workflow: ensure a database exists, get credentials, and run a migration via sandbox code execution.

```typescript
import { Recursiv } from '@recursiv/sdk';

const r = new Recursiv();

const PROJECT_ID = 'proj_123';

// 1. Ensure the database exists
const { data: db } = await r.databases.ensure({
  project_id: PROJECT_ID,
  name: 'main',
});
console.log(`Database "${db.name}" status: ${db.status}`);

// 2. Get connection credentials
const { data: creds } = await r.databases.getCredentials({
  project_id: PROJECT_ID,
  name: 'main',
});
console.log('Connection string:', creds.connection_string);

// 3. Run a migration in the sandbox
const { data: result } = await r.projects.executeCode(PROJECT_ID, {
  code: `
    import pg from 'pg';

    const client = new pg.Client({
      connectionString: '${creds.connection_string}',
    });
    await client.connect();

    await client.query(\`
      CREATE TABLE IF NOT EXISTS users (
        id SERIAL PRIMARY KEY,
        email TEXT UNIQUE NOT NULL,
        name TEXT NOT NULL,
        created_at TIMESTAMPTZ DEFAULT NOW()
      );
    \`);

    const result = await client.query('SELECT COUNT(*) FROM users');
    console.log('Users table ready. Row count:', result.rows[0].count);

    await client.end();
  `,
  language: 'typescript',
});

console.log('Migration output:', result.output);
```

## Using credentials with ORMs

The credentials object works directly with popular Node.js database libraries:

### With Drizzle

```typescript
import { drizzle } from 'drizzle-orm/node-postgres';
import pg from 'pg';

const { data: creds } = await r.databases.getCredentials({
  project_id: 'proj_123',
  name: 'main',
});

const pool = new pg.Pool({
  connectionString: creds.connection_string,
});

const db = drizzle(pool);
```

### With Prisma

```typescript
// Use the connection string as DATABASE_URL in your .env
const { data: creds } = await r.databases.getCredentials({
  project_id: 'proj_123',
  name: 'main',
});

process.env.DATABASE_URL = creds.connection_string;
// Then initialize Prisma normally
```