ObjectStackObjectStack

Example Apps

Run and explore the built-in example applications to learn ObjectStack hands-on

Example Apps

The monorepo ships three ready-to-run examples in examples/ that progressively demonstrate ObjectStack features — from a simple Todo app to a full CRM and a kitchen-sink reference. For a larger external enterprise reference, see the HotCRM repository.

Read these as reference, then build your own with an agent. Each example is the same kind of typed metadata Claude Code authors for you — so they double as a catalog of patterns to point your agent at ("build me a CRM like app-crm, but for support tickets"). To extend one, describe the change in plain language, let the agent edit the metadata, run os validate, and verify in the Console — the loop in Build with Claude Code.

app-todo

The simplest starting point. One object, basic CRUD, dashboards, seed data.

app-crm

A full CRM example (in-repo): multiple objects, views, apps, and seed data.

app-showcase

The kitchen-sink reference. Exercises most metadata types in one app.

Looking for a production server shape? Build an artifact with os compile, then run it with os start or os serve --artifact. The framework repo no longer contains a separate apps/objectos production host.


Quick Run

The fastest way to explore all examples at once:

git clone https://github.com/objectstack-ai/framework.git
cd framework
pnpm install
pnpm dev:showcase   # or: pnpm dev:todo / pnpm dev:crm

Each script starts one example's dev server. pnpm dev is an alias for pnpm dev:showcase.


app-todo — Your First App

Path: examples/app-todo/

The simplest complete ObjectStack application. Perfect for understanding the basics.

What's Inside

examples/app-todo/
├── objectstack.config.ts      # App manifest + metadata wiring
├── src/
│   ├── objects/
│   │   └── task.object.ts     # todo_task object: fields, validations, indexes
│   ├── actions/
│   │   └── task.actions.ts    # Complete, Start, Defer, Delete batch
│   ├── apps/
│   │   └── todo.app.ts        # Navigation: Tasks, Analytics
│   ├── views/                 # List / form views
│   ├── dashboards/
│   │   └── task.dashboard.ts  # Widget grid: stats, charts, lists
│   ├── reports/
│   │   └── task.report.ts     # Tabular + summary reports
│   ├── datasets/              # Saved query datasets for charts
│   ├── flows/
│   │   └── task.flow.ts       # Auto-assignment automation
│   ├── translations/          # Locale bundles
│   └── data/
│       └── index.ts           # Seed data via defineSeed()

Key Concepts Demonstrated

ConceptFileWhat You'll Learn
Object & Fieldstask.object.tsField.text(), Field.select(), field options, indexes
Seed Datasrc/data/index.tsdefineSeed() with upsert mode, 8 sample records
Actionstask.actions.tsScript actions, modal actions with params
App Navigationtodo.app.tsNavigation groups, object links, dashboard links
Automationtask.flow.tsAutolaunched flow with record-triggered logic

Run It

cd examples/app-todo
pnpm dev
# From the monorepo root
pnpm dev:todo

Code Walkthrough

1. Define an objectsrc/objects/task.object.ts:

import { ObjectSchema, Field } from '@objectstack/spec/data';

export const Task = ObjectSchema.create({
  name: 'todo_task',
  label: 'Task',
  pluralLabel: 'Tasks',
  icon: 'check-square',

  fields: {
    subject: Field.text({
      label: 'Subject',
      required: true,
      searchable: true,
    }),
    status: Field.select({
      label: 'Status',
      required: true,
      options: [
        { label: 'Not Started', value: 'not_started', color: '#808080', default: true },
        { label: 'In Progress', value: 'in_progress', color: '#3B82F6' },
        { label: 'Completed', value: 'completed', color: '#10B981' },
      ],
    }),
    due_date: Field.date({ label: 'Due Date' }),
    priority: Field.select({
      label: 'Priority',
      required: true,
      options: [
        { label: 'Low', value: 'low', color: '#60A5FA', default: true },
        { label: 'Normal', value: 'normal', color: '#10B981' },
        { label: 'High', value: 'high', color: '#F59E0B' },
        { label: 'Urgent', value: 'urgent', color: '#EF4444' },
      ],
    }),
  },
});

2. Wire it upobjectstack.config.ts:

import { defineStack } from '@objectstack/spec';
import * as objects from './src/objects';
import * as actions from './src/actions';
import * as apps from './src/apps';
import { TodoSeedData } from './src/data';

export default defineStack({
  manifest: {
    id: 'com.example.todo',
    namespace: 'todo',
    version: '2.0.0',
    type: 'app',
    name: 'Todo Manager',
  },

  objects: Object.values(objects),
  actions: Object.values(actions),
  apps: Object.values(apps),

  // Seed data authored with defineSeed() in src/data/
  data: TodoSeedData,
});

Seed data is authored separately with defineSeed() and wired in via the data key. For example, src/data/index.ts:

import { defineSeed } from '@objectstack/spec/data';
import { Task } from '../objects/task.object';

const tasks = defineSeed(Task, {
  mode: 'upsert',
  externalId: 'subject',
  records: [
    { subject: 'Learn ObjectStack', status: 'completed', priority: 'high' },
    { subject: 'Build a cool app', status: 'in_progress', priority: 'normal' },
  ],
});

export const TodoSeedData = [tasks];

3. The server auto-detects what you need: ObjectQL engine → InMemory driver → Hono HTTP server → REST API at /api/v1/data/todo_task.


HotCRM — Enterprise Scale (external repo)

Repo: github.com/objectstack-ai/hotcrm

A production-grade CRM demonstrating every ObjectStack feature. Use this as a reference for building real enterprise apps. It lives in a dedicated repository so it can evolve independently of the framework.

What's Inside

hotcrm/
├── objectstack.config.ts
├── src/
│   ├── objects/          # 10 objects: Account, Contact, Lead, Opportunity, ...
│   ├── actions/          # 15+ actions per domain
│   ├── agents/           # 5 AI agents: Sales, Service, Lead Enrichment, ...
│   ├── apis/             # Custom REST endpoints: lead-convert, pipeline-stats
│   ├── apps/             # CRM app with full navigation tree
│   ├── dashboards/       # Executive, Sales, Service dashboards
│   ├── data/             # Seed data for all objects
│   ├── flows/            # 5 automation flows: case escalation, approval, ...
│   ├── security/         # 5 permission sets: admin, sales rep, manager, ...
│   ├── rag/              # 4 RAG pipelines: product info, competitive intel, ...
│   ├── reports/          # 7 reports with tabular, summary, matrix formats
│   └── sharing/          # Sharing rules, business-unit hierarchy, org defaults

Features Showcased

FeatureDescription
10 Business ObjectsAccount, Contact, Lead, Opportunity, Case, Campaign, Product, Quote, Contract, Task
State MachineLead lifecycle: new → contacted → qualified → converted with Lead.state.ts
AI AgentsSales Assistant, Service Agent, Revenue Intelligence, Email Campaign, Lead Enrichment
RAG PipelinesProduct info, competitive intel, sales knowledge, support knowledge
Security Model5 permission sets (Admin, Manager, Rep, Agent, Marketing), sharing rules, business-unit hierarchy
AutomationLead conversion, case escalation, opportunity approval, campaign enrollment, quote generation
Custom APIsPOST /lead-convert, GET /pipeline-stats
Seed Data50+ realistic records across all objects

Run It

git clone https://github.com/objectstack-ai/hotcrm.git
cd hotcrm
pnpm install
pnpm dev        # Starts with auto-detected plugins

Or inspect its metadata without starting a server:

cd hotcrm
os info          # Show object/field/flow/agent counts
os validate      # Check schema correctness
os compile       # Build to dist/objectstack.json

app-showcase — Kitchen-Sink Reference

Path: examples/app-showcase/

A kitchen-sink workspace built for demonstration and debugging. It exercises nearly every metadata type, view type, and chart type in a single app — objects, views, apps, pages, dashboards, reports, datasets, flows, jobs, agents, security profiles, translations, themes, webhooks, and more. Use it as a living reference when you want to see how a particular metadata type is authored.

cd examples/app-showcase
pnpm dev

It is also the target of pnpm dev / pnpm dev:showcase from the monorepo root.


Composition Pattern

Compose apps and plugins in objectstack.config.ts, compile them into one artifact, then boot that artifact with the CLI.

import { composeStacks } from '@objectstack/spec';
import CrmApp from '../examples/app-crm/objectstack.config';
import TodoApp from '../examples/app-todo/objectstack.config';

// composeStacks merges objects, apps, and other metadata from each stack.
// The combined manifest is selected per the `manifest` option; the first
// stack's manifest is used by default. Duplicate object names throw unless
// you pass an `objectConflict` strategy ('override' | 'merge').
export default composeStacks([CrmApp, TodoApp]);

Short Names Are Canonical

Each app declares a namespace in its manifest, but the short object name is what you use everywhere — in engine.find(), hooks, formulas, lookups, REST URLs, and physical tables. The namespace is internal metadata used only for package provenance and cross-package disambiguation.

AppNamespaceObject namePhysical table
Todotodotodo_tasktodo_task
CRMcrmcrm_accountcrm_account

If two packages contribute objects with the same short name, the registry logs a warning. Resolve the collision by giving one object a package-prefixed short name (the convention the examples follow, e.g. crm_account, todo_task) rather than using FQN strings in user code.

Run It

os compile
OS_ARTIFACT_PATH=./dist/objectstack.json os start

Project Structure Conventions

All examples follow the same pattern. The recommended project layout — used by every example in examples/ — looks like this:

my-app/
├── objectstack.config.ts     # ← Entry point: manifest + metadata wiring
├── package.json
├── tsconfig.json
├── src/
│   ├── objects/              # Data models (required)
│   │   ├── index.ts          # Barrel export
│   │   └── *.object.ts       # One file per object
│   ├── actions/              # Buttons, batch operations (optional)
│   ├── flows/                # Automation logic (optional)
│   ├── apps/                 # Navigation definitions (optional)
│   ├── dashboards/           # Analytics dashboards (optional)
│   ├── reports/              # Report definitions (optional)
│   ├── agents/               # AI agents (optional)
│   └── data/                 # Seed data via defineSeed() (optional)
└── test/                     # Tests

os init scaffolds a minimal starter, not this full tree. It emits objectstack.config.ts plus a single object under src/objects/{namespace}_item.ts (e.g. my_app_item.ts) with a matching barrel src/objects/index.ts. Add the other directories above as your app grows.

Naming conventions:

  • Directories: plural (objects/, actions/, flows/)
  • Files: {name}.object.ts, {name}.actions.ts, {name}.flow.ts
  • Object names: snake_case short names with a package prefix (todo_task, crm_account)
  • Config keys: camelCase (maxLength, defaultValue, pluralLabel)

What's Next

On this page