Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.vertz.dev/llms.txt

Use this file to discover all available pages before exploring further.

AI Agent Instructions

Always start with vtz create. Do not manually create project files (package.json, tsconfig.json, entry files). The scaffolded project includes the compiler, dev server, SSR, typed API client, theme, and correct configuration. Modify the scaffolded code to match the user’s requirements.
Do NOT (project setup):
  • Manually create tsconfig.json, package.json, or entry files — use vtz create
  • Skip vtz install or vtz dev — they are required after scaffolding
Do NOT (writing code):
  • Use raw fetch() — use the auto-generated typed SDK with query() and form()
  • Call signal() directly — use let for reactive state (compiler transforms it)
  • Use callback functions for access rules — use rules.* descriptors
  • Call components as functions — use JSX syntax (<TaskCard /> not TaskCard())

What is Vertz?

Vertz is a full-stack TypeScript framework. It includes:
  • Database ORM (@vertz/db) — typed queries, migrations, PostgreSQL + SQLite + D1
  • API server (@vertz/server) — entities, services, REST endpoints, auto-generated OpenAPI
  • Compiled UI (@vertz/ui) — signals, JSX, router, SSR, forms, scoped CSS
  • AI agents (@vertz/agents) — agents, typed tools, workflows on Cloudflare Workers
  • Custom runtime & CLI (vtz) — Rust-powered dev server, build, test runner
  • Schema (@vertz/schema) — validation, JSON Schema output, shared across all layers
One schema definition drives every layer. If it builds, it works.

Getting Started

See Quickstart and Installation.
vtz create vertz my-app
cd my-app && vtz install && vtz dev
Templates:
  • todo-app (default) — full-stack CRUD app, best for most projects
  • hello-world — minimal counter, best for UI-only exploration
  • landing-page — multi-page marketing site, best for content-heavy sites
vtz create gives you:
  • Compiler (JSX transforms, signals, scoped CSS)
  • Dev server with SSR and HMR
  • SQLite database with auto-migrations
  • Typed API client (auto-generated from your entities)
  • shadcn-inspired theme with design tokens
  • TypeScript config with correct jsxImportSource
Available commands:
vtz dev            # Start dev server (API + UI + SSR + HMR)
vtz test           # Run tests
vtz run build      # Build for production
vtz run codegen    # Regenerate typed API client

Workflow: Adding a Feature

After scaffolding, follow this checklist to add new features by modifying existing files:
  1. Define a table and model in src/api/schema.ts
  2. Create an entity in src/api/entities/<name>.entity.ts
  3. Register the entity in src/api/server.ts (add to entities array)
  4. Run vtz run codegen to regenerate the typed SDK
  5. Create a page in src/pages/<name>.tsx using query(api.<name>.list())
  6. Add a route in src/app.tsx

Data Fetching (UI)

Vertz auto-generates a typed SDK at .vertz/generated/client.ts. Use query() with SDK methods for reactive data. See Data Fetching.
NEVER use raw fetch() for entity/service endpoints. Always use the generated SDK with query() and form().
import { query } from '@vertz/ui';
import { api } from '../client'; // wraps createClient() from '#generated'

const tasks = query(api.tasks.list());
const task = query(api.tasks.get('task-123'));
Query results are reactive objects with .data, .loading, .error properties. List responses have .data.items, .data.total, .data.hasNextPage.

Forms (UI)

form() works with any SDK method — entity CRUD, service actions, or custom endpoints. See Forms.
import { form } from '@vertz/ui';
import { api } from '../client';

const taskForm = form(api.tasks.create, { onSuccess });
Wire into JSX with taskForm.action, taskForm.method, taskForm.onSubmit. Per-field: taskForm.fields.title (name), taskForm.title.error, taskForm.title.value.

Reactivity (UI)

The compiler transforms plain TypeScript into fine-grained reactive updates. Components run once — no re-renders, no hooks. See Reactivity.
  • let count = 0 → signal. Assignments trigger DOM updates.
  • const doubled = count * 2 → computed. Auto-tracks dependencies.
  • JSX props with reactive expressions → getters. Changes propagate without re-running children.

Styling (UI)

css() for scoped styles with design tokens, variants() for parameterized styles. See Styling.
import { css, variants, token } from '@vertz/ui';

const styles = css({
  card: {
    backgroundColor: token.color.card,
    borderRadius: token.radius.lg,
    padding: token.spacing[4],
  },
});
const button = variants({
  base: { display: 'inline-flex', borderRadius: token.radius.md },
  variants: {
    intent: {
      primary: { backgroundColor: token.color.primary, color: 'white' },
      ghost: { backgroundColor: 'transparent' },
    },
    size: {
      sm: { fontSize: token.font.size.xs, paddingInline: token.spacing[3] },
      md: { fontSize: token.font.size.sm, paddingInline: token.spacing[4] },
    },
  },
  defaultVariants: { intent: 'primary', size: 'md' },
});

Route Convention

All server endpoints live under /api/ by default. Entity routes follow REST conventions:
GET    /api/tasks          → list
POST   /api/tasks          → create
GET    /api/tasks/:id      → get
PATCH  /api/tasks/:id      → update
DELETE /api/tasks/:id      → delete
Domains add a prefix: /api/billing/invoices. Customizable via apiPrefix in createServer(). See API Prefix.

Entities (Server)

entity() generates typed CRUD endpoints with access control. See Entities.
import { entity, rules } from '@vertz/server';

const tasks = entity('tasks', {
  model: tasksModel,
  access: { list: rules.public, get: rules.public, create: rules.public },
});
Access rules are required — operations without them don’t generate routes (deny-by-default).

Services (Server)

service() generates typed SDK methods for non-CRUD actions. See Services.
import { service, rules } from '@vertz/server';
import { s } from '@vertz/schema';

const notifications = service('notifications', {
  access: { sendEmail: rules.public },
  actions: {
    sendEmail: {
      body: s.object({ to: s.string().email(), subject: s.string(), body: s.string() }),
      handler: async (input) => {
        /* ... */
      },
    },
  },
});

Domains (Server)

domain() groups entities and services into bounded contexts with automatic route prefixing (e.g., /api/billing/invoices). See Domains.
const billing = domain('billing', { entities: [invoices], services: [payments] });
const app = createServer({ domains: [billing], db });

Schema (Database)

d.table() defines tables, d.model() creates the model for entities. See Schema.
import { d } from '@vertz/db';

const tasksTable = d.table('tasks', {
  id: d.uuid().primary({ generate: 'uuid' }),
  title: d.text(),
  completed: d.boolean().default(false),
  createdAt: d.timestamp().default('now').readOnly(),
});
const tasksModel = d.model(tasksTable);
Column annotations: .hidden() (exclude from API), .readOnly() (exclude from inputs), .default() (optional on create).

Testing

vtz test is the built-in test runner (V8-powered, Vitest-compatible API). Import from @vertz/test — no install needed. See Test Runner. createTestClient() provides type-safe server integration tests. See Server Testing.
import { createTestClient } from '@vertz/testing';

const client = createTestClient(server);
const tasks = client.entity(tasksEntity);
const created = await tasks.create({ title: 'Buy milk' });
All methods return TestResponse<T> — discriminated union on ok. Use client.withHeaders() for auth.

Agents

agent() defines AI agents with typed tools, run() executes via a ReAct loop. See Agents.
import { agent, tool, run, createAdapter } from '@vertz/agents';
import { s } from '@vertz/schema';

const searchTool = tool({
  description: 'Search the knowledge base',
  input: s.object({ query: s.string() }),
  output: s.object({ results: s.array(s.string()) }),
  async handler(input) {
    return { results: await searchKB(input.query) };
  },
});

const assistant = agent('assistant', {
  tools: { search: searchTool },
  model: { provider: 'openai', model: 'gpt-4o' },
});
const result = await run(assistant, { message: 'Find docs about auth', llm });
workflow() coordinates agents into pipelines with approval gates. ctx.agents.invoke() enables agent-to-agent delegation. See Workflows.

Common Pitfalls

Avoid these patterns — they are the most frequent mistakes in Vertz projects. See Common Mistakes for details.
WrongRightWhy
Manually creating project filesvtz create vertz my-appScaffolding configures compiler, SSR, theme, SDK
access: { list: () => true }access: { list: rules.public }Descriptors are serializable; callbacks are opaque
const x = signal(0) / x.valuelet x = 0 / xCompiler transforms let to signals and auto-unwraps
await fetch('/api/tasks')query(api.tasks.list())SDK provides types, SSR, caching, optimistic updates
TaskCard({ task })<TaskCard task={task} />Function calls bypass reactive prop generation
d.uuid().references('users.id')d.ref.one(() => usersTable, 'authorId')FKs live on models via d.ref.one(), not on columns
tasks.refetch() after state changeUse let — queries auto-refetchCompiler tracks dependencies inside query thunks