.do

db - Database Operations

Complete guide to database CRUD operations, queries, relationships, and semantic data management on the .do platform

The db primitive provides type-safe database operations for the .do platform. It offers a clean, semantic interface for managing data with full support for CRUD operations, complex queries, relationships, and integration with Schema.org types.

Overview

The db primitive is built on Payload CMS and provides a unified API for database operations across all collections. It features:

  • Type-Safe CRUD - Create, read, update, and delete operations
  • Advanced Queries - Filtering, sorting, pagination, field selection
  • Relationships - Query and manage related entities
  • Semantic Types - Full integration with $ semantic proxy
  • Entity Enhancement - Automatic enhancement with predicate methods
  • Validation - Collection name validation and security
  • RPC Architecture - All operations via RPC to API worker

Type Signatures

interface DB {
  list<T>(collection: string, options?: ListOptions): Promise<T[]>

  get<T>(collection: string, id: string): Promise<T | null>

  create<T>(collection: string, data: Partial<T>, options?: CreateOptions): Promise<T>

  update<T>(collection: string, id: string, data: Partial<T>, options?: UpdateOptions): Promise<T>

  delete(collection: string, id: string, options?: DeleteOptions): Promise<boolean>

  related<T>(entity: { collection: string; id: string }, relationship: PathProxy, targetCollection: string, options?: ListOptions): Promise<T[]>
}

Options Types

interface ListOptions {
  limit?: number
  offset?: number
  sort?: string
  where?: Record<string, unknown>
  include?: string[]
  select?: string[]
}

interface CreateOptions {
  skipValidation?: boolean
  returnFull?: boolean
}

interface UpdateOptions {
  skipValidation?: boolean
  merge?: boolean
}

interface DeleteOptions {
  cascade?: boolean
}

Basic CRUD Operations

List Records

Query multiple records from a collection with filtering and pagination.

// List all businesses
db.Businesses.list().then((businesses) => {
  console.log(businesses)
})

// List with limit
db.Orders.list({
  limit: 10,
}).then((recentOrders) => {
  console.log(recentOrders)
})

// List with pagination
db.Orders.list({
  limit: 20,
  offset: 20,
}).then((page2) => {
  console.log(page2)
})

// List with sorting
db.Products.list({
  sort: '-rating', // Descending
  limit: 10,
}).then((topRated) => {
  console.log(topRated)
})

// List with multiple sort fields
db.Orders.list({
  sort: '-createdAt,status',
}).then((sorted) => {
  console.log(sorted)
})

Get Single Record

Retrieve a single record by ID or slug.

// Get by ID
db.Orders.get('ord_abc123').then((order) => {
  console.log(order)
})

// Get by slug
db.Businesses.get('acme-inc').then((business) => {
  console.log(business)
})

// Handle not found
db.Users.get('usr_xyz').then((maybeUser) => {
  if (!maybeUser) {
    console.log('User not found')
  } else {
    console.log('Found user:', maybeUser.name)
  }
})

// Get with type inference
interface Order {
  id: string
  orderNumber: string
  total: number
  status: string
}

db.Orders.get<Order>('ord_123').then((typedOrder) => {
  if (typedOrder) {
    console.log(typedOrder.orderNumber) // Type-safe access
  }
})

Create Records

Create new records with semantic types.

// Simple create
db.People.create({
  name: 'Alice Johnson',
  email: '[email protected]',
  role: 'customer',
}).then((person) => {
  console.log('Created person:', person.id)
})

// Create with semantic type
db.Customers.create(
  $.Person({
    name: 'Bob Smith',
    email: '[email protected]',
    telephone: '+1-555-0123',
    jobTitle: 'Product Manager',
  })
).then((customer) => {
  console.log('Created customer:', customer.id)
})

// Create with nested data
db.Orders.create({
  orderNumber: 'ORD-2024-001',
  customer: {
    name: 'Charlie Davis',
    email: '[email protected]',
  },
  items: [
    {
      product: 'Laptop',
      quantity: 1,
      price: 1299.99,
    },
  ],
  total: 1299.99,
  status: 'pending',
}).then((order) => {
  console.log('Created order:', order.id)
})

// Create with options
db.Products.create(
  {
    name: 'Smart Widget',
    price: 99.99,
  },
  {
    skipValidation: false, // Ensure validation runs
  }
).then((validated) => {
  console.log('Created product:', validated.id)
})

Update Records

Update existing records by ID or slug.

// Simple update
db.Orders.update('ord_123', {
  status: 'shipped',
  shippedAt: new Date(),
})

// Partial update (merge)
db.Customers.update(
  'cus_456',
  {
    telephone: '+1-555-9999',
  },
  {
    merge: true, // Only update specified fields
  }
)

// Replace update
db.Products.update(
  'prod_789',
  {
    name: 'Updated Product',
    price: 149.99,
    stock: 50,
  },
  {
    merge: false, // Replace entire record
  }
)

// Update with semantic type
db.People.update(
  'per_123',
  $.Person({
    jobTitle: 'Senior Engineer',
    worksFor: $.Organization({ name: 'New Company' }),
  })
)

// Conditional update
db.Orders.get('ord_123').then((order) => {
  if (order && order.status === 'pending') {
    db.Orders.update('ord_123', {
      status: 'processing',
      processedAt: new Date(),
    })
  }
})

Delete Records

Delete records by ID or slug.

// Simple delete
db.Orders.delete('ord_123')

// Delete with cascade
db.Customers.delete('cus_456', {
  cascade: true, // Also delete related records
})

// Conditional delete
db.Orders.get('ord_789').then((order) => {
  if (order && order.status === 'cancelled') {
    db.Orders.delete('ord_789')
  }
})

// Bulk delete with list + delete
db.Orders.list({
  where: {
    createdAt: { lt: Date.now() - 365 * 24 * 60 * 60 * 1000 }, // 1 year ago
  },
}).then((oldOrders) => {
  oldOrders.forEach((order) => {
    db.Orders.delete(order.id)
  })
})

Query Options

Filtering with where

Filter records using MongoDB-style query operators.

// Equality
db.Orders.list({
  where: { status: 'active' },
}).then((activeOrders) => {
  console.log(activeOrders)
})

// Multiple conditions (AND)
db.Orders.list({
  where: {
    status: 'active',
    createdAt: { gte: Date.now() - 7 * 24 * 60 * 60 * 1000 },
  },
}).then((recentActive) => {
  console.log(recentActive)
})

// Comparison operators
db.Products.list({
  where: {
    price: { gte: 100, lte: 500 },
  },
}).then((expensive) => {
  console.log(expensive)
})

// In operator
db.Orders.list({
  where: {
    status: { in: ['pending', 'processing', 'shipped'] },
  },
}).then((specificStatuses) => {
  console.log(specificStatuses)
})

// Not equal
db.Orders.list({
  where: {
    status: { ne: 'cancelled' },
  },
}).then((nonCancelled) => {
  console.log(nonCancelled)
})

// Contains (for arrays and strings)
db.Articles.list({
  where: {
    tags: { contains: 'javascript' },
  },
}).then((tagged) => {
  console.log(tagged)
})

// Exists check
db.Customers.list({
  where: {
    telephone: { exists: true },
  },
}).then((withPhone) => {
  console.log(withPhone)
})

Available Operators:

  • eq or direct value - Equals
  • ne - Not equals
  • gt - Greater than
  • gte - Greater than or equal
  • lt - Less than
  • lte - Less than or equal
  • in - In array
  • nin - Not in array
  • contains - Contains (string or array)
  • exists - Field exists

Sorting with sort

Sort results by one or more fields.

// Ascending sort
const alphabetical = await db.list('products', {
  sort: 'name',
})

// Descending sort (prefix with -)
const newest = await db.list('orders', {
  sort: '-createdAt',
})

// Multiple sort fields
const sorted = await db.list('products', {
  sort: '-rating,name', // Sort by rating desc, then name asc
})

// Sort with pagination
const topProducts = await db.list('products', {
  sort: '-sales',
  limit: 20,
  offset: 0,
})

Pagination with limit and offset

Paginate results efficiently.

// First page
const page1 = await db.list('orders', {
  limit: 20,
  offset: 0,
})

// Second page
const page2 = await db.list('orders', {
  limit: 20,
  offset: 20,
})

// Helper function for pagination
async function getPage(collection: string, page: number, pageSize: number) {
  return await db.list(collection, {
    limit: pageSize,
    offset: (page - 1) * pageSize,
  })
}

const orders = await getPage('orders', 3, 25) // Page 3, 25 items per page

Field Selection with select

Select specific fields to reduce payload size.

// Select specific fields
const names = await db.list('customers', {
  select: ['name', 'email'],
})
// Returns: [{ name: '...', email: '...' }, ...]

// Select with ID (always included)
const basic = await db.list('products', {
  select: ['name', 'price'],
})
// Returns: [{ id: '...', name: '...', price: ... }, ...]

// Combine with filtering
const activeNames = await db.list('users', {
  where: { status: 'active' },
  select: ['name', 'email', 'lastLoginAt'],
})

Including Relationships with include

Populate related entities in the response.

// Include single relationship
const ordersWithCustomer = await db.list('orders', {
  include: ['customer'],
})
// order.customer will be populated object, not just ID

// Include multiple relationships
const fullOrders = await db.list('orders', {
  include: ['customer', 'items', 'shippingAddress'],
})

// Include nested relationships
const deepOrders = await db.list('orders', {
  include: ['customer', 'customer.company', 'items.product'],
})

// Combine with other options
const recent = await db.list('orders', {
  where: { status: 'shipped' },
  include: ['customer', 'items'],
  sort: '-shippedAt',
  limit: 10,
})

Relationships

Query entities related through semantic relationships.

// Query related entities
const brands = await db.related({ collection: 'businesses', id: 'biz_123' }, $.owns, 'brands')

// With options
const recentOrders = await db.related({ collection: 'customers', id: 'cus_456' }, $.placed, 'orders', {
  where: { status: 'completed' },
  sort: '-createdAt',
  limit: 10,
})

// Multiple hops
const employees = await db.related({ collection: 'businesses', id: 'biz_789' }, $.employs, 'people')

for (const employee of employees) {
  const projects = await db.related({ collection: 'people', id: employee.id }, $.worksOn, 'projects')
  console.log(`${employee.name} works on ${projects.length} projects`)
}

Creating Relationships

Link entities through semantic predicates.

// Create business
const business = await db.create(
  'businesses',
  $.Business({
    name: 'Tech Innovations Inc',
    industry: 'Technology',
  })
)

// Create brand
const brand = await db.create(
  'brands',
  $.Brand({
    name: 'InnoTech',
    logo: 'https://example.com/logo.png',
  })
)

// Link them via relationship
await business.owns(brand) // Using enhanced entity method

// Or manually using related()
const brands = await db.related({ collection: 'businesses', id: business.id }, $.owns, 'brands')

Relationship Patterns

Common relationship patterns in business applications.

// One-to-Many: Customer -> Orders
const customer = await db.create(
  'customers',
  $.Person({
    name: 'Alice',
    email: '[email protected]',
  })
)

const order1 = await db.create('orders', {
  customer: { id: customer.id },
  total: 99.99,
})

const order2 = await db.create('orders', {
  customer: { id: customer.id },
  total: 149.99,
})

// Query customer's orders
const customerOrders = await db.list('orders', {
  where: { 'customer.id': customer.id },
})

// Many-to-Many: Products <-> Categories
const product = await db.create('products', {
  name: 'Laptop',
  price: 1299.99,
  categories: ['electronics', 'computers'],
})

const electronics = await db.list('products', {
  where: { categories: { contains: 'electronics' } },
})

// Hierarchical: Organization -> Departments -> Teams
const org = await db.create('organizations', {
  name: 'Acme Corp',
})

const dept = await db.create('departments', {
  name: 'Engineering',
  organization: { id: org.id },
})

const team = await db.create('teams', {
  name: 'Frontend Team',
  department: { id: dept.id },
})

// Query hierarchy
const depts = await db.list('departments', {
  where: { 'organization.id': org.id },
})

for (const d of depts) {
  const teams = await db.list('teams', {
    where: { 'department.id': d.id },
  })
  console.log(`${d.name}: ${teams.length} teams`)
}

Integration with $ Semantic Proxy

Creating Records with Semantic Types

Use Schema.org types for rich, semantic data.

// Create Person with semantic type
const person = await db.create(
  'people',
  $.Person({
    name: 'John Doe',
    givenName: 'John',
    familyName: 'Doe',
    email: '[email protected]',
    jobTitle: 'Software Engineer',
    worksFor: $.Organization({
      name: 'Tech Corp',
    }),
  })
)

// Create Product with semantic type
const product = await db.create(
  'products',
  $.Product({
    name: 'Smart Widget Pro',
    description: 'Advanced smart widget with AI',
    sku: 'SWP-001',
    brand: $.Brand({ name: 'WidgetCo' }),
    offers: $.Offer({
      price: 299.99,
      priceCurrency: 'USD',
      availability: 'https://schema.org/InStock',
    }),
  })
)

// Create Event with semantic type
const event = await db.create(
  'events',
  $.Event({
    name: 'Tech Conference 2024',
    startDate: '2024-06-15T09:00:00Z',
    endDate: '2024-06-17T18:00:00Z',
    location: $.Place({
      name: 'Convention Center',
      address: {
        addressLocality: 'San Francisco',
        addressRegion: 'CA',
      },
    }),
    organizer: $.Organization({ name: 'Tech Events Inc' }),
  })
)

Querying with Semantic Filters

Filter using Schema.org properties.

// Find all Software Engineers
const engineers = await db.list('people', {
  where: {
    jobTitle: 'Software Engineer',
  },
})

// Find in-stock products
const available = await db.list('products', {
  where: {
    'offers.availability': 'https://schema.org/InStock',
  },
})

// Find events in specific location
const sfEvents = await db.list('events', {
  where: {
    'location.address.addressLocality': 'San Francisco',
  },
})

Advanced Query Patterns

Complex Filtering

Combine multiple conditions and operators.

// Date range with status
const recentActiveOrders = await db.list('orders', {
  where: {
    status: { in: ['pending', 'processing'] },
    createdAt: {
      gte: Date.now() - 30 * 24 * 60 * 60 * 1000, // 30 days ago
      lte: Date.now(),
    },
    total: { gte: 100 },
  },
})

// Text search with filters
const searchResults = await db.list('products', {
  where: {
    name: { contains: 'laptop' },
    price: { lte: 2000 },
    'offers.availability': 'https://schema.org/InStock',
  },
  sort: '-rating',
  limit: 20,
})

// Complex nested conditions
const qualified = await db.list('customers', {
  where: {
    'stats.totalOrders': { gte: 5 },
    'stats.totalSpent': { gte: 1000 },
    status: 'active',
    'preferences.marketing': true,
  },
})

Aggregation Patterns

While db doesn't have explicit aggregation, you can aggregate in code:

// Calculate total revenue
const orders = await db.list('orders', {
  where: { status: 'completed' },
  select: ['total'],
})

const totalRevenue = orders.reduce((sum, order) => sum + order.total, 0)

// Group by status
const allOrders = await db.list('orders', {
  select: ['status'],
})

const byStatus = allOrders.reduce(
  (acc, order) => {
    acc[order.status] = (acc[order.status] || 0) + 1
    return acc
  },
  {} as Record<string, number>
)

// Average rating
const products = await db.list('products', {
  select: ['rating'],
})

const avgRating = products.reduce((sum, p) => sum + (p.rating || 0), 0) / products.length

Batch Operations

Process multiple records efficiently.

// Batch create
const newProducts = [
  { name: 'Product 1', price: 10 },
  { name: 'Product 2', price: 20 },
  { name: 'Product 3', price: 30 },
]

const created = await Promise.all(newProducts.map((p) => db.create('products', p)))

// Batch update
const pendingOrders = await db.list('orders', {
  where: { status: 'pending' },
})

await Promise.all(pendingOrders.map((order) => db.update('orders', order.id, { status: 'processing' })))

// Batch delete
const oldOrders = await db.list('orders', {
  where: {
    status: 'completed',
    completedAt: { lt: Date.now() - 365 * 24 * 60 * 60 * 1000 },
  },
})

await Promise.all(oldOrders.map((order) => db.delete('orders', order.id)))

Complete Application Examples

E-Commerce Order System

Full order processing workflow.

// Create customer
const customer = await db.create(
  'customers',
  $.Person({
    name: 'Emily Johnson',
    email: '[email protected]',
    telephone: '+1-555-0200',
    address: $.PostalAddress({
      streetAddress: '456 Oak Street',
      addressLocality: 'Portland',
      addressRegion: 'OR',
      postalCode: '97201',
    }),
  })
)

// Browse products
const products = await db.list('products', {
  where: {
    'offers.availability': 'https://schema.org/InStock',
    price: { lte: 500 },
  },
  sort: '-rating',
  limit: 20,
})

// Add to cart
const cartItems = []
for (const product of products.slice(0, 2)) {
  cartItems.push({
    product: { id: product.id },
    quantity: 1,
    price: product.price,
  })
}

// Create order
const order = await db.create(
  'orders',
  $.Order({
    orderNumber: `ORD-${Date.now()}`,
    orderDate: new Date(),
    customer: { id: customer.id },
    orderedItem: cartItems,
    totalPrice: cartItems.reduce((sum, item) => sum + item.price, 0),
    priceCurrency: 'USD',
    orderStatus: 'https://schema.org/OrderProcessing',
  })
)

// Process payment
const payment = await db.create('payments', {
  order: { id: order.id },
  amount: order.totalPrice,
  currency: order.priceCurrency,
  status: 'pending',
})

// Update order status
await db.update('orders', order.id, {
  orderStatus: 'https://schema.org/OrderPaymentDue',
  payment: { id: payment.id },
})

// Query customer's order history
const customerOrders = await db.list('orders', {
  where: { 'customer.id': customer.id },
  sort: '-orderDate',
  include: ['orderedItem'],
})

CRM Contact Management

Customer relationship tracking.

// Create contact
const contact = await db.create(
  'contacts',
  $.Person({
    name: 'Sarah Williams',
    email: '[email protected]',
    telephone: '+1-555-0300',
    jobTitle: 'VP of Sales',
    worksFor: $.Organization({
      name: 'BigCorp Inc',
      industry: 'Manufacturing',
    }),
  })
)

// Log interaction
const interaction = await db.create('interactions', {
  contact: { id: contact.id },
  type: 'call',
  notes: 'Discussed product demo',
  date: new Date(),
  outcome: 'interested',
})

// Create opportunity
const opportunity = await db.create('opportunities', {
  name: 'BigCorp - Q1 Deal',
  contact: { id: contact.id },
  amount: 50000,
  stage: 'Qualification',
  closeDate: '2024-03-31',
  probability: 0.5,
})

// Track activities
const activities = await db.list('interactions', {
  where: { 'contact.id': contact.id },
  sort: '-date',
  limit: 10,
})

// Update opportunity stage
await db.update('opportunities', opportunity.id, {
  stage: 'Proposal',
  probability: 0.7,
})

// Find hot opportunities
const hotOpps = await db.list('opportunities', {
  where: {
    stage: { in: ['Proposal', 'Negotiation'] },
    amount: { gte: 10000 },
    closeDate: { lte: Date.now() + 30 * 24 * 60 * 60 * 1000 },
  },
  sort: '-amount',
  include: ['contact'],
})

Content Management System

Blog and content workflow.

// Create author
const author = await db.create(
  'authors',
  $.Person({
    name: 'Tech Blogger',
    email: '[email protected]',
    url: 'https://techblog.example.com/author/tech-blogger',
  })
)

// Create article draft
const article = await db.create(
  'articles',
  $.Article({
    headline: 'Getting Started with Semantic Web',
    description: 'Learn the basics of semantic web technologies',
    articleBody: 'Full article content here...',
    author: { id: author.id },
    dateCreated: new Date(),
    articleSection: 'Tutorial',
    keywords: ['semantic-web', 'rdf', 'linked-data'],
    status: 'draft',
  })
)

// Add images
const image = await db.create('media', {
  article: { id: article.id },
  url: 'https://example.com/article-image.jpg',
  caption: 'Semantic web diagram',
  type: 'image',
})

// Update article with image
await db.update('articles', article.id, {
  image: image.url,
})

// Publish article
await db.update('articles', article.id, {
  status: 'published',
  datePublished: new Date(),
})

// Query published articles
const recentArticles = await db.list('articles', {
  where: { status: 'published' },
  sort: '-datePublished',
  limit: 10,
  include: ['author'],
})

// Get articles by category
const tutorials = await db.list('articles', {
  where: {
    articleSection: 'Tutorial',
    status: 'published',
  },
  sort: '-datePublished',
})

// Search articles
const searchResults = await db.list('articles', {
  where: {
    headline: { contains: 'semantic' },
    status: 'published',
  },
})

Inventory Management

Track products and stock levels.

// Create warehouse
const warehouse = await db.create(
  'warehouses',
  $.Place({
    name: 'Main Warehouse',
    address: {
      streetAddress: '123 Storage Lane',
      addressLocality: 'Seattle',
      addressRegion: 'WA',
      postalCode: '98101',
    },
  })
)

// Add products with inventory
const product = await db.create(
  'products',
  $.Product({
    name: 'Widget Pro',
    sku: 'WGT-PRO-001',
    price: 49.99,
    inventory: {
      warehouse: { id: warehouse.id },
      quantity: 100,
      reorderPoint: 20,
    },
  })
)

// Record sale (decrease inventory)
const order = await db.create('orders', {
  product: { id: product.id },
  quantity: 5,
})

// Update inventory
const currentProduct = await db.get('products', product.id)
await db.update('products', product.id, {
  'inventory.quantity': currentProduct.inventory.quantity - 5,
})

// Check low stock
const lowStock = await db.list('products', {
  where: {
    'inventory.quantity': { lte: 20 },
  },
})

// Generate reorder report
for (const item of lowStock) {
  console.log(`Reorder needed: ${item.name} (${item.inventory.quantity} remaining)`)
}

// Record restocking
const restock = await db.create('restocks', {
  product: { id: product.id },
  warehouse: { id: warehouse.id },
  quantity: 50,
  date: new Date(),
})

await db.update('products', product.id, {
  'inventory.quantity': currentProduct.inventory.quantity + 50,
})

Transaction Patterns

While db doesn't have explicit transactions, you can implement transaction-like patterns:

// Rollback pattern
async function transferInventory(fromWarehouse: string, toWarehouse: string, productId: string, quantity: number) {
  // Get current state
  const product = await db.get('products', productId)
  const fromInv = product.inventory.find((i) => i.warehouse.id === fromWarehouse)
  const toInv = product.inventory.find((i) => i.warehouse.id === toWarehouse)

  if (!fromInv || fromInv.quantity < quantity) {
    throw new Error('Insufficient inventory')
  }

  try {
    // Decrease from source
    await db.update('products', productId, {
      inventory: product.inventory.map((i) => (i.warehouse.id === fromWarehouse ? { ...i, quantity: i.quantity - quantity } : i)),
    })

    // Increase at destination
    await db.update('products', productId, {
      inventory: product.inventory.map((i) => (i.warehouse.id === toWarehouse ? { ...i, quantity: i.quantity + quantity } : i)),
    })

    // Log transfer
    await db.create('transfers', {
      product: { id: productId },
      fromWarehouse: { id: fromWarehouse },
      toWarehouse: { id: toWarehouse },
      quantity,
      date: new Date(),
    })
  } catch (error) {
    // Rollback: restore original state
    await db.update('products', productId, {
      inventory: product.inventory,
    })
    throw error
  }
}

Performance Considerations

1. Use Filters Instead of Loading All Records

// ❌ Bad: Load all, filter in code
const allOrders = await db.list('orders')
const pending = allOrders.filter((o) => o.status === 'pending')

// ✅ Good: Filter at database level
const pending = await db.list('orders', {
  where: { status: 'pending' },
})

2. Select Only Needed Fields

// ❌ Bad: Load all fields
const orders = await db.list('orders')

// ✅ Good: Select specific fields
const orders = await db.list('orders', {
  select: ['orderNumber', 'status', 'total'],
})

3. Use Pagination for Large Datasets

// ❌ Bad: Load everything
const allProducts = await db.list('products')

// ✅ Good: Paginate
async function* paginateProducts(pageSize = 100) {
  let offset = 0
  while (true) {
    const page = await db.list('products', {
      limit: pageSize,
      offset,
    })
    if (page.length === 0) break
    yield page
    offset += pageSize
  }
}

for await (const page of paginateProducts()) {
  // Process page
}

4. Batch Operations with Promise.all

// ❌ Bad: Sequential updates
for (const order of orders) {
  await db.update('orders', order.id, { status: 'shipped' })
}

// ✅ Good: Parallel updates
await Promise.all(orders.map((order) => db.update('orders', order.id, { status: 'shipped' })))

5. Cache Frequently Accessed Data

// Simple in-memory cache
const cache = new Map()

async function getCachedProduct(id: string) {
  if (cache.has(id)) {
    return cache.get(id)
  }

  const product = await db.get('products', id)
  cache.set(id, product)
  return product
}

Common Pitfalls and Solutions

1. Forgetting to Handle Null Returns

// ❌ Bad: Assumes record exists
const order = await db.get('orders', 'ord_123')
console.log(order.total) // May throw if order is null

// ✅ Good: Check for null
const order = await db.get('orders', 'ord_123')
if (order) {
  console.log(order.total)
} else {
  console.log('Order not found')
}

2. Invalid Collection Names

// ❌ Bad: Invalid characters
await db.list('../../../secret-data') // Throws validation error

// ✅ Good: Valid collection name
await db.list('orders')

3. Incorrect Query Operators

// ❌ Bad: Wrong operator syntax
await db.list('orders', {
  where: { total: { greater_than: 100 } }, // Invalid operator
})

// ✅ Good: Correct operator
await db.list('orders', {
  where: { total: { gt: 100 } },
})

4. Forgetting to Await

// ❌ Bad: Missing await
const orders = db.list('orders') // Returns Promise, not data

// ✅ Good: With await
const orders = await db.list('orders')

Readonly vs Authenticated Mode

Readonly Mode (Anonymous)

Anonymous users can only read data:

// ✅ Available: List and get
const orders = await db.list('orders')
const order = await db.get('orders', 'ord_123')

// ❌ Blocked: Write operations
await db.create('orders', { ... })     // Error: Authentication required
await db.update('orders', id, { ... }) // Error: Authentication required
await db.delete('orders', id)          // Error: Authentication required

Authenticated Mode

All operations available with proper authentication:

// All CRUD operations
await db.create('orders', { ... })
await db.update('orders', id, { ... })
await db.delete('orders', id)

// Check authentication
const currentUser = user.current()
if (currentUser) {
  // Full access
  await db.create('orders', { ... })
}
  • $ - Semantic proxy for creating typed objects
  • on - Event handlers triggered by database changes
  • send - Publish events on database operations
  • user - Check permissions before database operations

Next Steps