Yeti - The Performance Platform for Agent-Driven Development. Schema-driven APIs, real-time streaming, and vector search. From prompt to production.
A fully interactive GraphQL explorer for a book catalog. Editable queries, mutations, live SSE subscriptions, syntax-highlighted editors, and real-time result merging — all generated from a single schema file.
demo-graphql demonstrates yeti's built-in GraphQL engine on a rich relational domain: Authors write Books, Publishers publish them, Readers leave Reviews, and Categories organize everything. The React frontend provides a two-panel IDE with syntax highlighting, one-click execution, and live subscription toggling. The backend is zero custom code — every query, mutation, subscription, and REST endpoint is auto-generated from graph.graphql.
GraphQL tooling typically requires a separate server (Apollo, Hasura, Postgraphile), a database, resolver boilerplate, and subscription infrastructure. Getting a working GraphQL explorer with mutations and live subscriptions usually means assembling four or five services.
demo-graphql collapses all of that into one yeti application:
- Zero resolvers — yeti auto-generates queries, mutations, and subscriptions from
@export(graphql: true)schema directives. No resolver code to write or maintain. - Nested relationships —
@relationshipdirectives produce joins automatically. QueryAuthor -> books -> reviewsin a single GraphQL request. - Live subscriptions via SSE — subscribe to any table and receive real-time updates as server-sent events. The frontend merges changes into displayed results automatically.
- Syntax-highlighted editor — editable query and mutation panels with highlight.js GraphQL grammar, transparent textarea overlay, and synchronized scrolling.
- Dual REST + GraphQL — every table is accessible via both GraphQL (
POST /demo-graphql/graphql) and REST (GET /demo-graphql/Book?limit=10). Same schema, two protocols, zero duplication. - Seed data included — 5 authors, 5 publishers, 10 books, 8 reviews, and 6 categories loaded on startup via
dataLoader. Explore immediately, no setup. - Single schema file — the entire data model, API surface, access rules, and transport configuration live in one 57-line GraphQL file.
cd ~/yeti/applications
git clone https://github.com/yetirocks/demo-graphql.gitRestart yeti. The frontend builds automatically on first load (npm run build via Vite) and is cached for subsequent starts.
Navigate to https://localhost:9996/demo-graphql/ in your browser. The two-panel IDE loads with a default query pre-filled.
curl -s -X POST https://localhost:9996/demo-graphql/graphql \
-H "Content-Type: application/json" \
-d '{"query": "{ Author(id: \"author-1\") { id name bio books { id title publishedYear } } }"}' | jqResponse:
{
"data": {
"Author": {
"id": "author-1",
"name": "Jane Austen",
"bio": "English novelist known primarily for her six major novels...",
"books": [
{
"id": "book-1",
"title": "Pride and Prejudice",
"publishedYear": 1813
},
{
"id": "book-2",
"title": "Sense and Sensibility",
"publishedYear": 1811
}
]
}
}
}curl -s -X POST https://localhost:9996/demo-graphql/graphql \
-H "Content-Type: application/json" \
-d '{"query": "mutation { updateAuthor(id: \"author-1\", data: { bio: \"English novelist and one of the most widely read writers in English literature.\" }) { id name bio } }"}' | jqResponse:
{
"data": {
"updateAuthor": {
"id": "author-1",
"name": "Jane Austen",
"bio": "English novelist and one of the most widely read writers in English literature."
}
}
}curl -s -X POST https://localhost:9996/demo-graphql/graphql \
-H "Content-Type: application/json" \
-d '{"query": "{ Book(id: \"book-1\") { title isbn price author { name country } publisher { name headquarters } reviews { rating title reviewer } } }"}' | jqResponse:
{
"data": {
"Book": {
"title": "Pride and Prejudice",
"isbn": "978-0141439518",
"price": 12.99,
"author": {
"name": "Jane Austen",
"country": "England"
},
"publisher": {
"name": "Penguin Random House",
"headquarters": "New York, USA"
},
"reviews": [
{
"rating": 5,
"title": "A timeless classic",
"reviewer": "BookLover42"
},
{
"rating": 4,
"title": "Delightful read",
"reviewer": "JaneAustenFan"
}
]
}
}
}curl -s "https://localhost:9996/demo-graphql/Book?limit=5" | jqResponse:
[
{
"id": "book-1",
"title": "Pride and Prejudice",
"isbn": "978-0141439518",
"publishedYear": 1813,
"genre": "Romance",
"price": 12.99,
"authorId": "author-1",
"publisherId": "pub-1"
},
{
"id": "book-2",
"title": "Sense and Sensibility",
"isbn": "978-0141439662",
"publishedYear": 1811,
"genre": "Romance",
"price": 11.99,
"authorId": "author-1",
"publisherId": "pub-1"
}
]# In one terminal — subscribe to Author changes
curl -N "https://localhost:9996/demo-graphql/Author?stream=sse"
# In another terminal — trigger an update
curl -s -X POST https://localhost:9996/demo-graphql/graphql \
-H "Content-Type: application/json" \
-d '{"query": "mutation { updateAuthor(id: \"author-2\", data: { bio: \"Updated bio for Asimov\" }) { id name } }"}'The SSE stream emits the updated Author record in real-time.
Browser (React + Vite)
|
+-- POST /demo-graphql/graphql -----> Yeti GraphQL Engine
+-- GET /demo-graphql/{Table} -----> Yeti REST Engine
+-- SSE Accept: text/event-stream -> Yeti SSE Transport
|
v
+------------------------------------------------+
| demo-graphql |
| |
| graph.graphql (schema) |
| +---------+ +-----------+ +----------+ |
| | Author | | Publisher | | Book | |
| +---------+ +-----------+ +----------+ |
| | | | | |
| +-- books -----+-- books-+ | |
| | | | |
| +----------+ +----------+ |
| | Review | | Category | |
| +----------+ +----------+ |
| |
| @export(rest, graphql, public: [read]) |
| @relationship (auto-joins) |
| @indexed (secondary indexes) |
+------------------------------------------------+
|
v
Yeti (embedded RocksDB, GraphQL engine, SSE broker)
Query path: Browser sends GraphQL POST -> yeti parses query against schema -> resolves fields + relationships from RocksDB -> returns JSON response.
Mutation path: Browser sends GraphQL mutation -> yeti validates input against schema -> writes to RocksDB -> broadcasts change via SSE -> returns mutated record.
Subscription path: Browser sends subscription with Accept: text/event-stream -> yeti opens SSE stream on the target table -> any mutation on that table pushes events to all subscribers -> frontend merges updates into displayed results.
The frontend provides a split-panel IDE for GraphQL operations:
- Syntax highlighting — custom highlight.js grammar for GraphQL keywords, types, variables, directives, and punctuation
- Editable textarea overlay — transparent textarea layered over a highlighted
<pre>block with synchronized scrolling - One-click execution — play button sends the query/mutation to
POST /demo-graphql/graphql - Status badges — real-time feedback: "Ready", "Error", "Success", "Live: message"
- JSON result rendering — response data displayed with color-coded syntax highlighting
Auto-generated from @export(graphql: true) directives. Every table gets:
| Operation | Example |
|---|---|
| Single record | { Author(id: "author-1") { name bio } } |
| All records | { Authors { id name country } } |
| Nested to-many | { Author(id: "author-1") { books { title } } } |
| Nested to-one | { Book(id: "book-1") { author { name } publisher { name } } } |
| Deep nesting | { Author(id: "author-1") { books { reviews { rating reviewer } } } } |
Auto-generated create, update, and delete mutations for every table:
| Operation | Example |
|---|---|
| Create | mutation { createBook(data: { id: "book-11", title: "Emma", isbn: "978-0141439587", authorId: "author-1" }) { id title } } |
| Update | mutation { updateAuthor(id: "author-1", data: { bio: "Updated bio" }) { id name bio } } |
| Delete | mutation { deleteReview(id: "review-1") { id } } |
The subscribe button in the UI:
- Executes the current query to populate initial results
- Extracts the target table from the query text
- Opens an SSE stream via
POST /demo-graphql/graphqlwithAccept: text/event-stream - Merges incoming events into the displayed result set (by matching record
id) - Flashes a "Live: message" badge on each update
Mutate a record in the bottom panel and watch the query results update in real-time in the top panel.
Every @export(rest: true) table also gets full REST endpoints:
| Endpoint | Methods | Description |
|---|---|---|
/demo-graphql/Author |
GET, POST | List/create authors |
/demo-graphql/Author/{id} |
GET, PUT, DELETE | Read/update/delete an author |
/demo-graphql/Publisher |
GET, POST | List/create publishers |
/demo-graphql/Publisher/{id} |
GET, PUT, DELETE | Read/update/delete a publisher |
/demo-graphql/Book |
GET, POST | List/create books |
/demo-graphql/Book/{id} |
GET, PUT, DELETE | Read/update/delete a book |
/demo-graphql/Review |
GET, POST | List/create reviews |
/demo-graphql/Review/{id} |
GET, PUT, DELETE | Read/update/delete a review |
/demo-graphql/Category |
GET, POST | List/create categories |
/demo-graphql/Category/{id} |
GET, PUT, DELETE | Read/update/delete a category |
All tables declare public: [read] in their @export directive. Read operations (queries, GET requests, SSE subscriptions) work without authentication. Write operations (mutations, POST/PUT/DELETE) require authentication in production.
| Field | Type | Indexed | Description |
|---|---|---|---|
id |
ID! | Primary key | Unique author identifier (e.g., "author-1") |
name |
String! | -- | Author's full name |
email |
String | Yes | Contact email |
bio |
String | -- | Biographical description |
country |
String | -- | Country of origin |
books |
[Book] | Relationship | Books written by this author (via authorId) |
Seed data: 5 authors — Jane Austen, Isaac Asimov, Agatha Christie, Gabriel Garcia Marquez, Haruki Murakami.
| Field | Type | Indexed | Description |
|---|---|---|---|
id |
ID! | Primary key | Unique publisher identifier (e.g., "pub-1") |
name |
String! | -- | Publisher name |
founded |
Int | -- | Year founded |
headquarters |
String | -- | Location of headquarters |
books |
[Book] | Relationship | Books published (via publisherId) |
Seed data: 5 publishers — Penguin Random House, HarperCollins, Simon & Schuster, Vintage Books, Knopf.
| Field | Type | Indexed | Description |
|---|---|---|---|
id |
ID! | Primary key | Unique book identifier (e.g., "book-1") |
title |
String! | -- | Book title |
isbn |
String! | Yes | ISBN-13 number |
publishedYear |
Int | -- | Year of publication |
genre |
String | Yes | Genre classification |
price |
Float | -- | Cover price (USD) |
authorId |
ID! | Yes | Foreign key to Author |
publisherId |
ID | Yes | Foreign key to Publisher |
author |
Author | Relationship | The author (via authorId) |
publisher |
Publisher | Relationship | The publisher (via publisherId) |
reviews |
[Review] | Relationship | Reader reviews (via bookId) |
Seed data: 10 books across 5 genres — Romance, Science Fiction, Mystery, Magical Realism, Literary Fiction. Prices range from $11.99 to $16.99.
| Field | Type | Indexed | Description |
|---|---|---|---|
id |
ID! | Primary key | Unique review identifier (e.g., "review-1") |
bookId |
ID! | Yes | Foreign key to Book |
rating |
Int! | -- | Rating (1-5 stars) |
title |
String | -- | Review headline |
content |
String | -- | Review body text |
reviewer |
String | -- | Reviewer handle |
createdAt |
String | -- | ISO 8601 timestamp |
book |
Book | Relationship | The reviewed book (via bookId) |
Seed data: 8 reviews across 6 books. Ratings range from 4 to 5 stars.
| Field | Type | Indexed | Description |
|---|---|---|---|
id |
ID! | Primary key | Unique category identifier (e.g., "cat-1") |
name |
String! | Yes | Category name |
description |
String | -- | Category description |
parentId |
ID | Yes | Parent category for hierarchy |
Seed data: 6 categories — Fiction (root), with Science Fiction, Mystery, Romance, Literary Fiction, and Magical Realism as children.
Category (self-referencing via parentId)
Fiction
+-- Science Fiction
+-- Mystery
+-- Romance
+-- Literary Fiction
+-- Magical Realism
Author --< Book >-- Publisher
|
Review
App configuration lives in Cargo.toml under [package.metadata.app]. There is no separate config.yaml or services.yaml.
[package]
name = "demo-graphql"
version = "1.0.0"
description = "Interactive GraphQL explorer with editable queries, mutations, and live SSE subscriptions"
[package.metadata.app]
schemas = "schemas/graph.graphql"
dataLoader = "data/*.json"
static = { path = "web", source = "source", spa = true, build = "npm install && npm run build" }| Key | Value | Purpose |
|---|---|---|
schemas |
schemas/graph.graphql |
Single schema defining all 5 tables with relationships |
dataLoader |
data/*.json |
Loads seed data from 5 JSON files on startup |
static.path |
web |
Serves built React app from web/ directory |
static.spa |
true |
SPA mode: serves index.html for all unmatched routes |
static.source / static.build |
source -> npm install && npm run build |
Auto-builds frontend from source/ on first load |
| Directive | Purpose |
|---|---|
@table(database: "demo-graphql") |
Creates a RocksDB-backed table in the demo-graphql database |
@export(rest: true, graphql: true, public: [read]) |
Generates REST endpoints, GraphQL operations, and allows unauthenticated reads |
@primaryKey |
Marks the id field as the primary key |
@indexed |
Creates a secondary index for filtered queries |
@relationship(from: "fieldId") |
To-one join: resolves a parent record from a foreign key |
@relationship(to: "fieldId") |
To-many join: resolves child records that reference this record |
demo-graphql/
+-- Cargo.toml # App configuration under [package.metadata.app]
+-- schemas/
| +-- graph.graphql # Author, Publisher, Book, Review, Category
+-- data/
| +-- authors.json # 5 seed authors
| +-- books.json # 10 seed books
| +-- publishers.json # 5 seed publishers
| +-- reviews.json # 8 seed reviews
| +-- categories.json # 6 seed categories (hierarchical)
+-- source/ # React/Vite frontend
+-- index.html
+-- package.json
+-- vite.config.ts
+-- tsconfig.json
+-- src/
+-- main.tsx # App entry point
+-- App.tsx # Thin shell -- wires auth gate + page
+-- api.ts # Fetch helpers
+-- types.ts # Shared TypeScript types
+-- utils.ts # JSON syntax highlighting utility
+-- components/
| +-- Footer.tsx # Shared UI primitives
+-- hooks/
| +-- useAuth.ts # Auth state hook (template)
+-- pages/
| +-- GraphqlPage.tsx # Query/mutation/subscription IDE
| +-- Login.tsx # Configurable login page (template)
+-- styles/
+-- _vars.css # Per-app brand colors and shared tokens
+-- yeti.css # Canonical Yeti stylesheet
+-- index.css # App-specific overrides
The src/ layout is the standard yeti UI app structure: a thin App.tsx, root utility modules (api.ts, types.ts, utils.ts), shared UI in components/, hooks in hooks/, pages in pages/ (including the bundled Login.tsx), and stylesheets in styles/. yeti.css is the canonical stylesheet shared across all yeti apps; _vars.css holds this app's brand tokens; index.css carries app-specific overrides.
All tables declare public: [read], so queries and SSE subscriptions are open. To require login on writes -- or gate the explorer entirely -- declare a [package.metadata.auth] section in Cargo.toml (methods, JWT, OAuth providers, role rules). The bundled Login.tsx page and useAuth hook can wrap the IDE with three lines:
const auth = useAuth()
if (auth === null) return <Loading/>
if (!auth) return <Login/>
return <GraphqlPage/>The Login component takes optional logo, title, subtitle, and redirectUri props for branding.
cd ~/yeti/applications/demo-graphql/source
# Install dependencies
npm install
# Start dev server with HMR
npm run dev
# Build for production (outputs to ../web/)
npm run buildThe Vite dev server proxies API requests to the running yeti instance. Edit queries and components with hot module replacement.
| demo-graphql | Traditional GraphQL Setup | |
|---|---|---|
| Schema to API | One .graphql file, zero resolvers |
Schema + resolvers + type definitions |
| Relationships | @relationship directive, auto-resolved |
Manual resolver functions for each join |
| Subscriptions | @export(graphql: true) enables SSE automatically |
WebSocket server, PubSub library, subscription resolvers |
| REST fallback | @export(rest: true) adds REST alongside GraphQL |
Separate REST layer or none |
| Database | Embedded RocksDB, zero config | PostgreSQL/MongoDB setup, migrations, connection pooling |
| Seed data | dataLoader: data/*.json loads on startup |
Migration scripts, seed commands, fixture files |
| Frontend | SPA auto-built from source/ directory |
Separate build pipeline, deploy step |
| Auth | public: [read] in schema directive |
Auth middleware, context injection, per-resolver checks |
| Infrastructure | Single yeti binary | GraphQL server + database + subscription transport |
Built with Yeti | The Performance Platform for Agent-Driven Development
