Schema-Driven Full Stack
Define once, generate everything. One schema creates database tables, REST endpoints, OpenAPI specs, TypeScript types, and the full UI — living code that stays in sync.
How many times have you defined "User" in your application? Once in the database migration. Again in the ORM model. Again in the API response type. Again in the frontend state. Again in the form validation. Again in the API documentation. Six definitions of the same concept, all waiting to drift apart.
The Single Source of Truth
Schema-driven development inverts this pattern. You define your data model once, and the system generates everything else:
- Database tables — Migrations generated from schema changes
- REST endpoints — CRUD operations with proper HTTP semantics
- TypeScript types — Compile-time safety across the stack
- OpenAPI specifications — Always-accurate API documentation
- MCP tools — AI-callable interfaces automatically
- State managers — Frontend business logic that mirrors the API
- UI components — Forms, tables, validation rules
Change the schema — the entire stack regenerates. Not scaffolding you edit. Living code that stays in sync.
Real-World Use Cases
Rapid Prototyping to Production
You're building an MVP. Define your Product, Order, and Customer schemas. In 15–30 minutes, you have a working admin dashboard with CRUD operations, form validation, and type-safe API calls. The prototype is the production code — just add business logic where needed.
Evolving Data Models
Requirements change. Your User model needs a preferences field. You add it to the schema. The migration is generated. The API endpoint accepts the new field. The TypeScript types update. The settings form renders the new input. One change, zero coordination overhead.
Multi-Platform Applications
You're building for web, iOS, and Android. The schema generates a React state manager for web, and the same contract generates Swift and Kotlin types for mobile. All three platforms share the exact same API contract, validated at compile time.
How Existing Tools Approach This
Many tools tackle pieces of this problem. Few attempt the full vertical integration:
Prisma
Schema-first ORM that generates TypeScript types and database migrations from a single .prisma file. Excellent for the database-to-API layer, but stops at the backend. No UI generation, no frontend types without additional tooling.
Hasura
Auto-generates a GraphQL API from your PostgreSQL schema. Includes subscriptions, permissions, and relationships. However, the schema lives in the database — you're generating the API from tables, not tables from a higher-level definition.
Supabase
Provides auto-generated REST and GraphQL APIs from PostgreSQL, plus TypeScript type generation. Strong for rapid development, but the frontend layer (state management, UI components) remains manual.
tRPC
End-to-end type safety between backend and frontend without code generation. Procedures defined on the server are callable with full types on the client. Powerful, but requires both ends to be TypeScript and doesn't generate UI or database schema.
OpenAPI Generator
Generates client SDKs in dozens of languages from OpenAPI specs. But the spec itself is typically hand-written or derived from code annotations — it's not the source of truth, it's a byproduct.
AWS Amplify
GraphQL schema defines your data model; Amplify generates DynamoDB tables, resolvers, and JavaScript/TypeScript client libraries. Close to full-stack generation, but tightly coupled to AWS services.
The Two-Layer Architecture
True schema-driven development requires two fully separated layers, all generated from the same source:
1. Presentation Layer
React, React Native, Vue, Svelte — your choice. Generated components handle rendering but contain no business logic. A UserForm component knows how to render a user form, but not what to do when it's submitted.
2. State Manager Layer
Business logic lives here. The state manager knows that creating a user requires validation, then an API call, then updating local state, then showing a notification. It orchestrates, the presentation layer renders. It also handles API contract mirroring, caching, token management, and retry logic — the mechanical complexity of talking to the backend, generated from the same schema that defines the backend itself.
"Not scaffolding you edit. Living code that stays in sync."
Why This Matters
The average full-stack feature requires changes in 6-10 files across 3-4 layers. Each layer has its own conventions, its own testing requirements, its own opportunities for bugs. Developers spend more time on plumbing than on product.
Schema-driven development collapses this complexity. The schema is the feature definition. Everything else is derived. When generated code meets 80% of your needs perfectly and the remaining 20% has clear extension points, development velocity transforms.
How It Works
In an Application Operating System, you define a schema once as a JSON document. The system derives everything else:
// jsen-sym-schema-for-posts/schema.jsen.json
{
"kSTable": "posts",
"kJFields": {
"kSType": "J",
"kJKeys": {
"slug": {
"kSType": "S",
"kRFormat": "^[a-z0-9]+(\\-[a-z0-9]+)*$",
"kSBytes": "64",
"kSRequired": "1"
},
"title": {
"kSType": "S",
"kSBytes": "255",
"kSRequired": "1"
},
"body": {
"kSType": "S",
"kRFormat": "((^$)|(\\.(html|txt)$))",
"kSRequired": "0"
}
}
},
"kSPrimary": "slug",
"kAIndexes": ["title"]
}
Reference this schema in your service configuration:
// config.json
{
"databases": {
"blogdb": {
"$aHCoreTables": {
"posts": "jsen-sym-schema-for-posts"
}
}
},
"apis": {
"blogapi": {
"$aHRoutes": {
// These routes are automatically generated from the schema:
"$db:search:/blogdb.posts": {
"path": ["jsen-sym-plugin-default-handle"],
"expects": "$db:search:/blogdb.posts -> expects",
"returns": "$db:search:/blogdb.posts -> returns"
},
"$db:select:/blogdb.posts": { ... },
"$db:insert:/blogdb.posts": { ... },
"$db:update:/blogdb.posts": { ... },
"$db:delete:/blogdb.posts": { ... }
}
}
}
}
The notation $db:search:/blogdb.posts -> expects tells the system to derive the API contract from the schema. What gets generated:
- Database table — With proper types, constraints, and indexes
- CRUD endpoints — Search, select, insert, update, delete
- API contracts — Request/response schemas for each operation
- Validation rules — Regex patterns, byte limits, required fields
- OpenAPI specification — Auto-generated documentation
Add a field to the schema — the database migrates, the API accepts it, the contract updates. Remove a field — the same cascade happens in reverse.
Beyond CRUD
Schema-driven doesn't mean only generated code. Complex business logic, custom endpoints, specialized UI — these still get written by hand. The schema handles the 80% that's predictable, freeing you to focus on the 20% that's unique.
The generated code provides extension points: plugin pipelines for before/after operations, customizable components for UI, middleware chains for endpoints. You're not fighting the framework; you're extending it.