ObjectStackObjectStack

Common Patterns

Top 10 patterns for building applications with ObjectStack — CRUD, search, auth, realtime, and more

Common Patterns Guide

This guide covers the most common patterns you will use when building applications with ObjectStack. Each pattern includes a complete, copy-pasteable example.

Import: import { defineStack, defineView, defineApp, defineFlow, defineAgent } from '@objectstack/spec'


1. CRUD Object Definition

Define a basic object with common fields for create, read, update, and delete operations.

import { defineStack } from '@objectstack/spec';

export default defineStack({
  objects: [
    {
      name: 'project',
      label: 'Project',
      fields: {
        title: { label: 'Title', type: 'text', required: true, maxLength: 200 },
        description: { label: 'Description', type: 'textarea' },
        status: { label: 'Status', type: 'select', options: [
          { label: 'Planning', value: 'planning', default: true },
          { label: 'Active', value: 'active' },
          { label: 'Complete', value: 'complete' },
          { label: 'Archived', value: 'archived' }
        ]},
        owner: { label: 'Owner', type: 'lookup', reference: 'user' },
        due_date: { label: 'Due Date', type: 'date' },
        budget: { label: 'Budget', type: 'currency', currencyConfig: { precision: 2, defaultCurrency: 'USD' } },
      }
    }
  ]
});
import { defineStack } from '@objectstack/spec';

export default defineStack({
  objects: {
    project: {
      label: 'Project',
      fields: {
        title: { label: 'Title', type: 'text', required: true, maxLength: 200 },
        description: { label: 'Description', type: 'textarea' },
        status: { label: 'Status', type: 'select', options: [
          { label: 'Planning', value: 'planning', default: true },
          { label: 'Active', value: 'active' },
          { label: 'Complete', value: 'complete' },
          { label: 'Archived', value: 'archived' }
        ]},
        owner: { label: 'Owner', type: 'lookup', reference: 'user' },
        due_date: { label: 'Due Date', type: 'date' },
        budget: { label: 'Budget', type: 'currency', currencyConfig: { precision: 2, defaultCurrency: 'USD' } },
      },
    },
  },
});

2. Master-Detail Relationship

Create a parent-child relationship where child records are cascaded on delete.

{
  objects: [
    {
      name: 'order',
      label: 'Order',
      fields: {
        order_number: { label: 'Order #', type: 'autonumber', autonumberFormat: 'ORD-{0000}' },
        customer: { label: 'Customer', type: 'lookup', reference: 'contact' },
        total: { label: 'Total', type: 'summary', summaryOperations: ['sum'] },
        status: { label: 'Status', type: 'select', options: [
          { label: 'Draft', value: 'draft', default: true },
          { label: 'Submitted', value: 'submitted' },
          { label: 'Fulfilled', value: 'fulfilled' }
        ]},
      }
    },
    {
      name: 'order_line',
      label: 'Order Line',
      fields: {
        order: { label: 'Order', type: 'master_detail', reference: 'order' },
        product: { label: 'Product', type: 'lookup', reference: 'product' },
        quantity: { label: 'Qty', type: 'number', min: 1, required: true },
        unit_price: { label: 'Unit Price', type: 'currency' },
        line_total: { label: 'Line Total', type: 'formula', expression: 'quantity * unit_price' },
      }
    }
  ]
}

3. Search & Filtered List View

Create a list view with pre-configured filters and column display.

import { defineView } from '@objectstack/spec';

export const taskBoard = defineView({
  name: 'open_tasks',
  label: 'Open Tasks',
  object: 'task',
  type: 'list',
  listType: 'grid',
  columns: [
    { field: 'title', width: 300 },
    { field: 'status', width: 120 },
    { field: 'priority', width: 100 },
    { field: 'assigned_to', width: 150 },
    { field: 'due_date', width: 120 }
  ],
  defaultFilters: {
    status: { $ne: 'closed' }
  },
  defaultSort: [
    { field: 'priority', order: 'desc' },
    { field: 'due_date', order: 'asc' }
  ]
});

4. Kanban Board View

Display records as a kanban board grouped by a status field.

import { defineView } from '@objectstack/spec';

export const kanban = defineView({
  name: 'task_board',
  label: 'Task Board',
  object: 'task',
  type: 'list',
  listType: 'kanban',
  kanbanConfig: {
    groupByField: 'status',
    cardTitle: 'title',
    cardSubtitle: 'assigned_to',
    showCount: true
  }
});

5. Application with Navigation

Define an application with a structured navigation menu.

import { defineApp } from '@objectstack/spec';

export const crm = defineApp({
  name: 'crm',
  label: 'CRM',
  description: 'Customer Relationship Management',
  navigation: [
    {
      type: 'object',
      object: 'contact',
      label: 'Contacts',
      icon: 'users'
    },
    {
      type: 'object',
      object: 'deal',
      label: 'Deals',
      icon: 'dollar-sign'
    },
    {
      type: 'object',
      object: 'activity',
      label: 'Activities',
      icon: 'activity'
    },
    {
      type: 'dashboard',
      dashboard: 'sales_dashboard',
      label: 'Dashboard',
      icon: 'bar-chart'
    }
  ]
});

6. Automated Flow (Record-Triggered)

Create an automation that fires when a record is created or updated.

import { defineFlow } from '@objectstack/spec';

export const assignmentNotification = defineFlow({
  name: 'task_assignment_notification',
  label: 'Task Assignment Notification',
  type: 'autolaunched',
  trigger: {
    type: 'record_change',
    object: 'task',
    event: 'after_update',
    conditions: {
      field: 'assigned_to',
      changed: true
    }
  },
  steps: [
    {
      type: 'action',
      name: 'send_notification',
      action: 'send_email',
      config: {
        to: '{{record.assigned_to.email}}',
        subject: 'Task Assigned: {{record.title}}',
        body: 'You have been assigned to task "{{record.title}}".'
      }
    }
  ]
});

7. Approval Workflow

Define a multi-step approval process for records.

{
  objects: [{
    name: 'expense_report',
    label: 'Expense Report',
    fields: {
      title: { label: 'Title', type: 'text', required: true },
      amount: { label: 'Amount', type: 'currency' },
      status: { label: 'Status', type: 'select', options: [
        { label: 'Draft', value: 'draft', default: true },
        { label: 'Submitted', value: 'submitted' },
        { label: 'Approved', value: 'approved' },
        { label: 'Rejected', value: 'rejected' }
      ]},
      submitted_by: { label: 'Submitted By', type: 'lookup', reference: 'user' },
      approved_by: { label: 'Approved By', type: 'lookup', reference: 'user' },
    },
    enable: {
      trackHistory: true,
      apiEnabled: true
    }
  }],
  flows: [{
    name: 'expense_approval',
    label: 'Expense Approval',
    type: 'autolaunched',
    trigger: {
      type: 'record_change',
      object: 'expense_report',
      event: 'after_update'
    },
    steps: [
      {
        type: 'decision',
        name: 'check_amount',
        conditions: [
          { name: 'auto_approve', condition: '{{record.amount}} < 100' },
          { name: 'needs_approval', condition: '{{record.amount}} >= 100' }
        ]
      }
    ]
  }]
}

8. AI Agent Configuration

Configure an AI agent with specific tools and instructions.

import { defineAgent } from '@objectstack/spec';

export const supportAgent = defineAgent({
  name: 'support_agent',
  label: 'Customer Support Agent',
  role: 'assistant',
  model: 'gpt-4o',
  instructions: `You are a helpful customer support agent. 
    You can search for customer records, look up order status, 
    and create support tickets. Always be polite and professional.`,
  tools: [
    {
      type: 'object_query',
      object: 'contact',
      description: 'Search customer records'
    },
    {
      type: 'object_query',
      object: 'order',
      description: 'Look up order status'
    },
    {
      type: 'object_create',
      object: 'support_ticket',
      description: 'Create a support ticket'
    }
  ],
  temperature: 0.3,
  maxTokens: 2048
});

9. Pagination Pattern

Implement pagination for large datasets using offset-based or cursor-based pagination.

Offset-Based (Simple)

// Page 1
const page1 = {
  object: 'contact',
  fields: ['id', 'name', 'email'],
  orderBy: [{ field: 'name', order: 'asc' }],
  limit: 25,
  offset: 0
};

// Page 2
const page2 = { ...page1, offset: 25 };

// Page 3
const page3 = { ...page1, offset: 50 };

Cursor-Based (Scalable)

// First page
const firstPage = {
  object: 'activity',
  fields: ['id', 'type', 'description', 'created_at'],
  orderBy: [{ field: 'created_at', order: 'desc' }],
  limit: 50,
  keyset: {
    field: 'id',
    order: 'desc'
  }
};

// Next page (using cursor from previous response)
const nextPage = {
  ...firstPage,
  keyset: {
    ...firstPage.keyset,
    after: 'last_id_from_previous_page'
  }
};

10. Field-Level Security

Restrict field visibility and editability based on user profiles.

{
  objects: [{
    name: 'employee',
    label: 'Employee',
    fields: {
      name: { label: 'Name', type: 'text', required: true },
      email: { label: 'Email', type: 'email', required: true },
      department: { label: 'Department', type: 'lookup', reference: 'department' },
      // Sensitive fields with encryption
      salary: { label: 'Salary', type: 'currency',
        hidden: true,  // Hidden from default views
        encryptionConfig: {
          algorithm: 'aes-256-gcm',
          keyRotation: true
        }
      },
      ssn: { label: 'SSN', type: 'text',
        hidden: true,
        maskingRule: {
          strategy: 'partial',
          visibleChars: 4,
          maskChar: '*',
          position: 'end'
        },
        encryptionConfig: {
          algorithm: 'aes-256-gcm',
          keyRotation: true
        }
      },
    }
  }]
}

Putting It All Together

Combine these patterns into a complete defineStack() configuration:

import { defineStack } from '@objectstack/spec';

export default defineStack({
  objects: [
    // Pattern 1: CRUD objects
    { name: 'project', label: 'Project', fields: {/* ... */} },
    // Pattern 2: Master-detail
    { name: 'task', label: 'Task', fields: {/* ... */} },
  ],
  views: [
    // Pattern 3: List views
    // Pattern 4: Kanban boards
  ],
  apps: [
    // Pattern 5: Navigation
  ],
  flows: [
    // Pattern 6: Automations
    // Pattern 7: Approval workflows
  ],
  agents: [
    // Pattern 8: AI agents
  ]
});
import { defineStack } from '@objectstack/spec';

export default defineStack({
  objects: {
    // Pattern 1: CRUD objects
    project: { label: 'Project', fields: {/* ... */} },
    // Pattern 2: Master-detail
    task: { label: 'Task', fields: {/* ... */} },
  },
  apps: {
    // Pattern 5: Navigation
  },
  flows: {
    // Pattern 6: Automations
    // Pattern 7: Approval workflows
  },
  agents: {
    // Pattern 8: AI agents
  },
});

Next Steps:

On this page