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:crmEach 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
| Concept | File | What You'll Learn |
|---|---|---|
| Object & Fields | task.object.ts | Field.text(), Field.select(), field options, indexes |
| Seed Data | src/data/index.ts | defineSeed() with upsert mode, 8 sample records |
| Actions | task.actions.ts | Script actions, modal actions with params |
| App Navigation | todo.app.ts | Navigation groups, object links, dashboard links |
| Automation | task.flow.ts | Autolaunched flow with record-triggered logic |
Run It
cd examples/app-todo
pnpm dev# From the monorepo root
pnpm dev:todoCode Walkthrough
1. Define an object — src/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 up — objectstack.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 defaultsFeatures Showcased
| Feature | Description |
|---|---|
| 10 Business Objects | Account, Contact, Lead, Opportunity, Case, Campaign, Product, Quote, Contract, Task |
| State Machine | Lead lifecycle: new → contacted → qualified → converted with Lead.state.ts |
| AI Agents | Sales Assistant, Service Agent, Revenue Intelligence, Email Campaign, Lead Enrichment |
| RAG Pipelines | Product info, competitive intel, sales knowledge, support knowledge |
| Security Model | 5 permission sets (Admin, Manager, Rep, Agent, Marketing), sharing rules, business-unit hierarchy |
| Automation | Lead conversion, case escalation, opportunity approval, campaign enrollment, quote generation |
| Custom APIs | POST /lead-convert, GET /pipeline-stats |
| Seed Data | 50+ 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 pluginsOr 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.jsonapp-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 devIt 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.
| App | Namespace | Object name | Physical table |
|---|---|---|---|
| Todo | todo | todo_task | todo_task |
| CRM | crm | crm_account | crm_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 startProject 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/ # Testsos 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_caseshort names with a package prefix (todo_task,crm_account) - Config keys:
camelCase(maxLength,defaultValue,pluralLabel)
What's Next
- CLI Reference — All available commands
- Data Modeling Guide — Deep dive into objects and fields
- Developer Guide — Full learning path from objects to AI agents