npm package: @monkeyhub/sdk
Description: TypeScript SDK for MonkeyHub. Zero dependencies, works in Node.js 18+.
Latest version: 0.2.2

# @monkeyhub/sdk

TypeScript SDK for MonkeyHub. Zero dependencies, works in Node.js 18+.

## Install

```bash
npm install @monkeyhub/sdk
```

## Quick Start

```typescript
import { Monkey } from '@monkeyhub/sdk'

const monkey = new Monkey('mk_live_...')

// Get a collection (auto-registers on first use)
const users = monkey.collection<User>('users')

// Save
await users.save({ id: 'user_1', name: 'Ada', email: 'ada@example.com' })

// Fetch
const user = await users.findOne('user_1')
```

## MonkeyDB

### Collections

Collections are typed key-value stores backed by DynamoDB. They auto-register on first use — no setup step required.

```typescript
interface Order {
  id: string
  customerId: string
  status: string
  total: number
  createdAt: number
}

const orders = monkey.collection<Order>('orders', {
  key: { hashKey: 'id' },
  indexes: [
    'status',
    { name: 'by-customer', hashKey: 'customerId', rangeKey: 'createdAt' },
  ],
})
```

### Databases

Collections live inside a **database**. If you don't specify one, they go into `default`. Use databases to logically group collections.

```typescript
// These are equivalent:
monkey.collection('users')
monkey.db('default').collection('users')

// Organize by domain:
const analytics = monkey.db('analytics')
const events = analytics.collection<Event>('events')
const metrics = analytics.collection<Metric>('metrics')

// List collections in a database:
const collections = await analytics.listCollections()
```

### Save Items

```typescript
// Single item
const saved = await orders.save({ id: 'ord_1', customerId: 'cust_1', status: 'pending', total: 49.99, createdAt: Date.now() })

// Batch (up to 25 per request, auto-chunked for larger arrays)
const savedItems = await orders.save([
  { id: 'ord_2', customerId: 'cust_1', status: 'pending', total: 29.99, createdAt: Date.now() },
  { id: 'ord_3', customerId: 'cust_2', status: 'shipped', total: 89.99, createdAt: Date.now() },
])
```

`save()` requires the full key field and returns the saved item(s). Use `create()` for auto-key collections where the key is optional.

### Auto-Generated Keys

Set `hashKeyType: 'auto'` and use `create()` to insert items without providing a key. The server generates a UUID and returns the full item:

```typescript
const logs = monkey.db('telemetry').collection<Log>('logs', {
  key: { hashKey: 'id', hashKeyType: 'auto' },
})

// create() — key is optional, server generates it
const log = await logs.create({ level: 'info', message: 'deployed v2.1' })
console.log(log.id) // "a1b2c3d4-..."

// Batch create
const items = await logs.create([
  { level: 'info', message: 'starting' },
  { level: 'warn', message: 'slow query' },
])

// save() — key is required, for upserts with explicit IDs
await logs.save({ id: 'custom_id', level: 'error', message: 'oops' })
```

`create()` accepts `Partial<T>` — the hash key field is optional, everything else is up to you. The return type is always the full `T` with the generated key populated.

### Fetch Items

```typescript
// Single item by key
const order = await orders.findOne('ord_1')

// Batch fetch by keys
const items = await orders.find(['ord_1', 'ord_2', 'ord_3'])

// For collections with a range key:
const item = await orders.findOne('parent_key', 'sort_key')
```

`findOne` returns `null` if the item doesn't exist.

### Query

Query by primary key or a declared index:

```typescript
// By index
const pending = await orders.query({
  index: 'status',
  hashKey: 'pending',
})

// Compound index with range condition
const recentOrders = await orders.query({
  index: 'by-customer',
  hashKey: 'cust_1',
  rangeKey: Date.now() - 86400000,
  rangeKeyOp: '>=',
  limit: 20,
  reverse: true,
})

// Primary key query
const exact = await orders.query({ hashKey: 'ord_1' })

// Pagination
const page1 = await orders.query({ index: 'status', hashKey: 'shipped', limit: 10 })
const page2 = await orders.query({ index: 'status', hashKey: 'shipped', limit: 10, cursor: page1.cursor })
```

Range key operators: `=`, `<`, `<=`, `>`, `>=`, `begins_with`.

### Remove Items

```typescript
await orders.remove('ord_1')

// With range key:
await orders.remove('parent_key', 'sort_key')
```

### Drop a Collection

Permanently deletes all items and removes the collection config. Requires typing the collection name to confirm:

```typescript
await orders.drop('orders')
```

### Backups

Export a collection to NDJSON in MonkeyBuckets:

```typescript
const backup = await orders.backup()
console.log(backup.id, backup.status) // "bak_abc123", "pending"

// Check status later
const info = await orders.getBackup(backup.id)
console.log(info.status, info.itemCount)

// List all backups for this collection
const backups = await orders.listBackups()
```

### Key Schema Options

| Option | Values | Description |
|--------|--------|-------------|
| `hashKey` | field name | Primary key field (default: `"id"`) |
| `hashKeyType` | `"string"`, `"number"`, `"auto"` | Key type. `"auto"` generates UUIDs server-side |
| `rangeKey` | field name | Optional sort key for composite keys |

### Index Types

**Simple indexes** — a single field name becomes a hash-only index:

```typescript
indexes: ['status', 'region']
```

**Compound indexes** — hash + optional range key for sorted queries:

```typescript
indexes: [
  { name: 'by-customer-date', hashKey: 'customerId', rangeKey: 'createdAt' },
]
```

Up to 5 indexes per collection (varies by plan).

## Error Handling

All API errors throw typed error classes:

```typescript
import { MonkeyNotFoundError, MonkeyValidationError, MonkeyRateLimitError } from '@monkeyhub/sdk'

try {
  await users.save({ /* missing key */ })
} catch (err) {
  if (err instanceof MonkeyValidationError) {
    console.log(err.message)
  }
  if (err instanceof MonkeyRateLimitError) {
    // Auto-retried by default (3 attempts with backoff)
  }
}
```

## Client Options

```typescript
const monkey = new Monkey('mk_live_...', {
  baseUrl: 'https://api.monkeyhub.ai',  // default
  timeout: 30000,                         // request timeout in ms
  retry: {
    maxRetries429: 3,    // rate limit retries
    maxRetries503: 3,    // service unavailable retries
    maxRetriesNetwork: 1, // network error retries
  },
})
```

## API Keys

- `mk_live_*` — full read/write access
- `mk_pub_*` — read-only access (cannot write data, create collections, or drop)

Generate keys in the [MonkeyHub dashboard](https://monkeyhub.ai/dashboard) or via the API.