---
title: Example Apps
description: Run and explore the built-in example applications to learn ObjectStack hands-on
---

import { CheckSquare, Building2, BarChart3, Server } from 'lucide-react';

# Example Apps

The monorepo includes 2 ready-to-run examples that progressively demonstrate ObjectStack features — from a simple Todo app to a reusable BI plugin template. For a full enterprise reference, see the [HotCRM repository](https://github.com/objectstack-ai/hotcrm).

<Cards>
  <Card
    icon={<CheckSquare />}
    title="app-todo"
    description="The simplest starting point. One object, basic CRUD, dashboards, seed data."
  />
  <Card
    icon={<Building2 />}
    title="HotCRM (external)"
    href="https://github.com/objectstack-ai/hotcrm"
    description="Full-featured CRM: 10+ objects, AI agents, flows, security, sharing rules."
  />
  <Card
    icon={<BarChart3 />}
    title="plugin-bi"
    description="A reusable plugin template. Shows how to package metadata for distribution."
  />
</Cards>

<Callout type="info">
  **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.
</Callout>

---

## Quick Run

The fastest way to explore all examples at once:

```bash
git clone https://github.com/nickstenning/spec.git
cd spec
pnpm install
pnpm studio
```

Open [http://localhost:3000/_studio/](http://localhost:3000/_studio/) — the root `objectstack.config.ts` aggregates Todo + CRM + BI into one dev workspace.

---

## 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     # Task object: 10 fields, validations, indexes
│   ├── actions/
│   │   └── task.actions.ts    # Complete, Start, Defer, Delete batch
│   ├── apps/
│   │   └── todo.app.ts        # Navigation: Tasks, Analytics
│   ├── dashboards/
│   │   └── task.dashboard.ts  # Widget grid: stats, charts, lists
│   ├── reports/
│   │   └── task.report.ts     # Tabular + summary reports
│   └── flows/
│       └── task.flow.ts       # Auto-assignment automation
```

### Key Concepts Demonstrated

| Concept | File | What You'll Learn |
|:--------|:-----|:------------------|
| Object & Fields | `task.object.ts` | `Field.text()`, `Field.select()`, field options, indexes |
| Seed Data | `objectstack.config.ts` | `data` array 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

<Tabs items={['Standalone', 'From Root']}>
<Tab value="Standalone">
```bash
cd examples/app-todo
pnpm dev
```
</Tab>
<Tab value="From Root">
```bash
# Already included in root objectstack.config.ts
pnpm studio
```
</Tab>
</Tabs>

### Code Walkthrough

**1. Define an object** — `src/objects/task.object.ts`:

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

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

  fields: {
    subject: Field.text({
      label: 'Subject',
      required: true,
      searchable: true,
    }),
    status: {
      type: 'select',
      label: 'Status',
      required: true,
      options: [
        { label: 'Not Started', value: 'not_started', default: true },
        { label: 'In Progress', value: 'in_progress' },
        { label: 'Completed', value: 'completed' },
      ],
    },
    due_date: Field.date({ label: 'Due Date' }),
    priority: {
      type: 'select',
      label: 'Priority',
      options: [
        { label: 'Low', value: 'low' },
        { label: 'Normal', value: 'normal' },
        { label: 'High', value: 'high' },
        { label: 'Urgent', value: 'urgent' },
      ],
    },
  },
});
```

**2. Wire it up** — `objectstack.config.ts`:

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

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 loaded at startup
  data: [{
    object: 'task',
    mode: 'upsert',
    externalId: 'subject',
    records: [
      { subject: 'Learn ObjectStack', status: 'completed', priority: 'high' },
      { subject: 'Build a cool app', status: 'in_progress', priority: 'normal' },
    ],
  }],
});
```

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

---

## HotCRM — Enterprise Scale (external repo)

**Repo:** [github.com/objectstack-ai/hotcrm](https://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, ...
│   ├── profiles/         # 5 security profiles: admin, sales rep, manager, ...
│   ├── rag/              # 4 RAG pipelines: product info, competitive intel, ...
│   ├── reports/          # 7 reports with tabular, summary, matrix formats
│   └── sharing/          # Sharing rules, role hierarchy, org defaults
```

### Features 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 profiles (Admin, Manager, Rep, Agent, Marketing), sharing rules, role 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

```bash
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:

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

---

## plugin-bi — Reusable Plugin

**Path:** `examples/plugin-bi/`

A minimal plugin template showing how to package metadata for distribution. Plugins use `type: 'plugin'` in their manifest and can be composed into any host app.

```typescript
// examples/plugin-bi/objectstack.config.ts
import { defineStack } from '@objectstack/spec';

export default defineStack({
  manifest: {
    id: 'com.example.bi',
    namespace: 'bi',
    version: '1.0.0',
    type: 'plugin',        // ← Plugin, not app
    name: 'BI Plugin',
    description: 'Business Intelligence dashboards and analytics',
  },
  objects: [],
  dashboards: [],
});
```

**Key difference from apps:** Plugins don't run standalone — they're loaded by a host app via `AppPlugin`:

```typescript
import BiPlugin from '../plugin-bi/objectstack.config';
new AppPlugin(BiPlugin)  // Load into host
```

---

## Composition Pattern

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

```typescript
import { defineStack } from '@objectstack/spec';
import CrmApp from 'hotcrm/objectstack.config';  // from https://github.com/objectstack-ai/hotcrm
import TodoApp from '../examples/app-todo/objectstack.config';
import BiPlugin from '../examples/plugin-bi/objectstack.config';

export default defineStack({
  manifest: {
    id: 'com.objectstack.server',
    version: '1.0.0',
    type: 'app',
  },
  imports: [
    CrmApp,
    TodoApp,
    BiPlugin,
  ],
});
```

### 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` | `task` | `task` |
| CRM | `crm` | `account` | `account` |
| BI | `bi` | `report` | `report` |

If two packages contribute objects with the same short name, the registry logs a warning. Resolve the collision by renaming one object's short `name` (for example, `crm_account`) rather than using FQN strings in user code.

### Run It

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

---

## Project Structure Conventions

All examples follow the same pattern. When you run `os init`, you get this structure:

```
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)
│   └── rag/                  # RAG pipelines (optional)
└── test/                     # Tests
```

**Naming conventions:**
- **Directories:** plural (`objects/`, `actions/`, `flows/`)
- **Files:** `{name}.object.ts`, `{name}.actions.ts`, `{name}.flow.ts`
- **Object names:** `snake_case` short names (`task`, `sales_order`, `account`)
- **Config keys:** `camelCase` (`maxLength`, `defaultValue`, `pluralLabel`)

## What's Next

- [CLI Reference](/docs/getting-started/cli) — All available commands
- [Data Modeling Guide](/docs/guides/data-modeling) — Deep dive into objects and fields
- [Developer Guide](/docs/getting-started/quick-start) — Full learning path from objects to AI agents
