MonkeyDB
A key-value database with automatic multi-tenancy, typed queries, and sparse indexing — without touching partition keys or internal index slots.
Collection Creation
Create a typed collection with optional indexes. Collections are registered automatically on first use.
// Simple indexes
const users = db.collection<User>('users', { indexes: ['email', 'role'] })
// Compound index (hash + range key)
const orders = db.collection<Order>('orders', {
indexes: [
'status',
{ name: 'byCustomer', hashKey: 'customerId', rangeKey: 'createdAt' },
],
})
// Custom primary key
const events = db.collection<Event>('events', {
key: { hashKey: 'streamId', rangeKey: 'sequence' },
})
// With vector search
const articles = db.collection<Article>('articles', {
search: { fields: ['title', 'body'] },
})Declared indexes map to internal GSI slots. Queries against indexed fields are fast (GSI lookup). The number of indexes per collection is limited by your plan tier.
CRUD Operations
save()
Upsert one or many items. Auto-chunks batches larger than 25 items.
// Single item
await users.save({ id: 'user_01', email: 'jerry@monkey.io', role: 'admin' })
// Batch — auto-chunks at 25
await users.save([
{ id: 'user_01', email: 'jerry@monkey.io', role: 'admin' },
{ id: 'user_02', email: 'george@monkey.io', role: 'member' },
])findOne()
Fetch a single item by ID. Sub-10ms latency.
const jerry = await users.findOne('user_01')find()
Batch fetch by multiple IDs. Auto-chunks requests exceeding 100 keys.
const batch = await users.find(['user_01', 'user_02', 'user_03'])remove()
Hard delete an item by ID.
await users.remove('user_01')Query Interface
Queries use hashKey for fast indexed lookups and filter for post-read filtering.
interface QueryOptions {
hashKey: string | number // Required — the indexed field value
rangeKey?: string | number // Optional range key value
rangeKeyOp?: '=' | 'begins_with' | '<' | '<=' | '>' | '>='
index?: string // Index name (omit for primary key)
filter?: Record<string, { [op: string]: any }>
limit?: number // Max 100, server-enforced
reverse?: boolean // Newest first
cursor?: string // Pagination cursor
}Fast query (indexed)
// Hits GSI directly — fast and efficient
await orders.query({ hashKey: 'paid', index: 'status' })Combined query
// GSI lookup + post-read filter
await orders.query({
hashKey: 'paid',
index: 'status',
filter: { amount: { '>': 50 } },
limit: 20,
reverse: true,
})hashKey are rejected with 400 Bad Request. This prevents accidental table scans.Pagination
All queries return a cursor for pagination. Pass it to the next query to get the next page.
let cursor: string | undefined
do {
const result = await users.query({
hashKey: 'admin',
index: 'role',
limit: 20,
cursor,
})
processItems(result.items)
cursor = result.cursor
} while (cursor)Vector Search
Enable semantic search on any collection. MonkeyHub automatically generates embeddings using OpenAI text-embedding-3-small and stores vectors in S3.
// Enable at collection creation
const articles = db.collection<Article>('articles', {
search: { fields: ['title', 'body'] },
})
// Save items — embeddings are generated automatically
await articles.save({
id: 'post_01',
title: 'Getting started with MonkeyHub',
body: 'MonkeyHub is a backend-as-a-service...',
})
// Search by natural language
const results = await articles.search({
text: 'how to set up a database',
topK: 10,
})
// results.items = [{ item: { id, title, body }, score: 0.92, distance: 0.08 }, ...]Limits
Indexes per plan
| Baby Monkey (Free) | Chimp ($19/mo) | Mandrill ($49/mo) | Silverback ($199/mo) | |
|---|---|---|---|---|
| Indexes / collection | 1 | 2 | 3 | 5 |
| Max write cost | 2x base | 3x base | 4x base | 6x base |
Other limits
| Limit | Value |
|---|---|
| Max item size | 400 KB |
| Max query limit | 100 (server-enforced) |
| Max batch save | 25 items per call (SDK auto-chunks larger batches) |
| Max batch find | 100 keys per call (SDK auto-chunks larger batches) |