ObjectStackObjectStack

Your First Project

Scaffold a standalone ObjectStack project with npm, understand what was generated, extend the data model, call the REST API, and build a deployable artifact — no monorepo checkout, no AI agent required.

Your First Project

This is the hands-on path for developers building on ObjectStack from the published npm packages — you never clone the framework repository. In about ten minutes you will scaffold a project, run it, add a field by hand, call the generated REST API, and compile the artifact you would deploy.

Two ways to build. ObjectStack is designed AI-first: normally an agent authors the metadata and you verify it. This page walks the same ground manually so you understand every file the agent would touch — useful for evaluating the platform, wiring CI, or working without an agent. The two paths produce identical projects.

Prerequisites

ToolMinimumCheck
Node.js22+node --version
A package managernpm 9+ / pnpm 8+ / yarn / bunnpm --version

That's all. A standalone project does not need pnpm, TypeScript pre-installed, or a database server — the default template runs on an in-memory driver, and TypeScript comes in as a dev dependency.

1. Scaffold

npm create objectstack@latest my-app
cd my-app

The scaffolder (create-objectstack) does four things:

  1. Copies a template into my-app/ and rewrites its identity — the npm package name, the manifest name/displayName, and a namespace derived from your project name (my-appmy_app). Every object in the template is renamed to carry that prefix (my_app_note), which is what os validate later enforces.
  2. Installs dependencies (pnpm if available, otherwise npm).
  3. Installs the AI skills bundle (npx skills add objectstack-ai/objectstack/skills --all) so a coding agent is productive in the project from the first prompt.
  4. Writes AGENTS.md (and .github/copilot-instructions.md) with the project conventions — including the rule to run npm run validate after every metadata change.

Steps 3–4 cost nothing if you never use an agent; skip step 3 (the skills bundle) with --skip-skills if you prefer — AGENTS.md is always written.

Templates

TemplateSourceDescription
blank (default)bundled, works offlineOne object, REST API — a clean slate
todoremoteUniversal task & project management starter
complianceremoteCompliance posture & evidence management (SOC2 / ISO27001)
contentremoteContent marketing pipeline — editorial calendar & channel ROI
contractsremotePost-signature CLM — approvals, obligations, renewals
procurementremoteSource-to-pay — vendors, POs, receipts, invoice matching
npm create objectstack@latest my-app -- --template todo

Remote templates are fetched from the objectstack-ai/templates repository at scaffold time and need network access; blank is bundled inside the npm package and always works offline.

os init (from @objectstack/cli) is an alternative scaffolder with app / plugin / empty templates — see the CLI reference. npm create objectstack is the recommended entry point for new standalone projects.

2. What was generated

my-app/
├── objectstack.config.ts        # defineStack() — the single entry point
├── objectstack.manifest.json    # name, namespace, specVersion
├── package.json
├── tsconfig.json
├── AGENTS.md                    # conventions for coding agents
└── src/
    └── objects/
        ├── index.ts             # barrel — re-exports every object
        └── note.object.ts       # a sample object

Two files matter most:

objectstack.config.ts wires everything together. There is no filename-suffix magic — metadata exists in the app only if it is imported here:

objectstack.config.ts
import { defineStack } from '@objectstack/spec';
import { ConnectorRestPlugin } from '@objectstack/connector-rest';
import { ConnectorOpenApiPlugin } from '@objectstack/connector-openapi';
import { ConnectorMcpPlugin } from '@objectstack/connector-mcp';
import * as objects from './src/objects/index.js';

export default defineStack({
  manifest: {
    id: 'my-app',
    namespace: 'my_app',
    version: '0.1.0',
    type: 'app',
    name: 'My App',
    // Protocol major this app is authored against (ADR-0087 load-time check).
    engines: { protocol: '^17' },
  },
  // `automation` runs flows and, per ADR-0097, materializes declarative
  // `connectors:` entries at boot. The three generic executors below register
  // their `rest` / `openapi` / `mcp` provider factories with it.
  requires: ['automation'],
  plugins: [
    new ConnectorRestPlugin(),
    new ConnectorOpenApiPlugin(),
    new ConnectorMcpPlugin(),
  ],
  objects: Object.values(objects),
});

The REST API needs no configuration — it is on by default for every object with apiEnabled. The plugins: array ships the three generic connector executors, so you can integrate an external REST / OpenAPI / MCP service as pure metadata: add a connectors: entry naming a provider and the automation capability materializes it into a live connector at boot — see Automation → Flows.

src/objects/note.object.ts is a complete data model — schema, validation, API surface, and searchability in one typed definition:

src/objects/note.object.ts
import { ObjectSchema, Field } from '@objectstack/spec/data';

export const Note = ObjectSchema.create({
  name: 'my_app_note',          // namespace-prefixed, snake_case
  label: 'Note',
  pluralLabel: 'Notes',
  icon: 'sticky-note',

  fields: {
    title: Field.text({ label: 'Title', required: true, searchable: true, maxLength: 200 }),
    body: Field.textarea({ label: 'Body' }),
  },

  sharingModel: 'private',      // org-wide default — an explicit, authored decision
  enable: { apiEnabled: true, searchable: true },
});

The packages you depend on

A standalone project uses a handful of published packages — this is the entire framework surface you install:

PackageWhat it does
@objectstack/specThe protocol: defineStack, ObjectSchema, Field.* builders, all Zod schemas. Your metadata imports only this.
@objectstack/runtimeThe kernel that interprets metadata at runtime. Brings the database drivers with it — you do not install one separately.
@objectstack/plugin-hono-serverThe HTTP server that mounts the generated REST API.
@objectstack/connector-rest · -openapi · -mcpThe three generic connector executors — register the rest / openapi / mcp provider factories so declarative connectors: entries materialize (ADR-0097).
@objectstack/cli (dev)The os / objectstack CLI: dev, validate, build, start.

No driver package is listed because none is installed directly: @objectstack/runtime depends on driver-sql, driver-sqlite-wasm and driver-memory, and the CLI carries them too. objectstack dev resolves SQLite by default; point OS_DATABASE_URL at Postgres / MySQL / MongoDB for production — no code changes.

3. Run it

npm run dev            # objectstack dev — rebuild + server restart on change

Or with the visual Console (data browser, metadata explorer, API docs):

npx os dev --ui        # then open http://localhost:3000/_console/

4. Call your API

The REST API is derived from the object metadata — you wrote no routes. Data endpoints run under the same permission model as the UI, so first sign in as the dev admin the server seeds on an empty database (admin@objectos.ai / admin123):

# Sign in once — stores the session cookie
curl -c cookies.txt -X POST http://localhost:3000/api/v1/auth/sign-in/email \
  -H "Content-Type: application/json" \
  -d '{"email": "admin@objectos.ai", "password": "admin123"}'

# Create a record
curl -b cookies.txt -X POST http://localhost:3000/api/v1/data/my_app_note \
  -H "Content-Type: application/json" \
  -d '{"title": "Hello ObjectStack", "body": "Created via the generated API"}'

# Query records (filtering, sorting, pagination built in)
curl -b cookies.txt "http://localhost:3000/api/v1/data/my_app_note?select=title&sort=-created_at&top=10"

See the Data API for the full CRUD, batch, and analytics surface, and the Client SDK for the typed TypeScript client (@objectstack/client).

5. Extend the model

Add a field and a second object by hand — the same edit an agent would make.

src/objects/note.object.ts
  fields: {
    title: Field.text({ label: 'Title', required: true, searchable: true, maxLength: 200 }),
    body: Field.textarea({ label: 'Body' }),
    status: Field.select({
      label: 'Status',
      options: [
        { label: 'Draft', value: 'draft', default: true },
        { label: 'Published', value: 'published' },
      ],
    }),
  },

New objects follow the same pattern: create src/objects/<name>.object.ts with a name of <namespace>_<short_name>, then re-export it from src/objects/index.ts (the barrel is what the config imports).

After every metadata change, run the gate:

npm run validate

os validate checks schema compliance, CEL predicate scoping, and widget bindings — the classes of mistakes that otherwise fail silently at runtime. See Validating Metadata.

Beyond pass/fail, npx os lint --score grades the data model 0–100 against the platform's design conventions (relationship patterns, missing select options, roll-ups, name fields) — a quick health check as the model grows.

6. Build the deployable artifact

npm run build          # objectstack build → dist/objectstack.json

The artifact is a portable, self-describing deployment unit: one JSON file containing your entire app. Any server with @objectstack/cli can boot it — no TypeScript, no build step on the host:

npx os start --artifact ./dist/objectstack.json --port 8080

Database URL, auth secret, and environment identity stay outside the artifact and are injected at boot via flags or OS_* environment variables — which is exactly what makes it container-friendly.

7. Hand the project to your agent

You just did by hand what an agent does in seconds — and the scaffolder already prepared the project for that handoff: the skills bundle is installed (step 1.3) and AGENTS.md routes each task to the right skill and enforces the validate gate. Open the project in Claude Code (or Cursor / Copilot) and describe the next feature instead of typing it:

Add a status filter to the note list view, a dashboard with a metric of published notes, and a daily flow that archives notes older than 90 days. Run npm run validate when done.

Each of those lands in a different metadata domain (UI, dashboards, automation) — the agent loads the matching skill per task. The full loop, and a per-domain prompt catalog, is on Build with Claude Code. If you upgrade @objectstack/spec later, re-run npx skills add objectstack-ai/objectstack/skills --all to keep the agent in sync with the schemas you run.

Next steps

On this page