GraphQL API Layer — Architecture Document
- **Performance Prophet** → Health metrics streaming, trend-based subscriptions - **Thought Mesh** → Distributed real-time state propagation - **Cross-Script Bridge** → WebSocket-based real-time translation - **Dream Weaver** → Event-driven lifecycle with state transitions
Full Public Reader
GraphQL API Layer — Architecture Document
## Dream Origin
Dream ID: `dream_202601260133_1b112f`
Pattern connections: Supabase integration, streaming pipelines, API performance, cross-project pollination
---
1. Design Philosophy
This GraphQL layer is built on patterns observed across the project ecosystem:
- Performance Prophet → Health metrics streaming, trend-based subscriptions
- Thought Mesh → Distributed real-time state propagation
- Cross-Script Bridge → WebSocket-based real-time translation
- Dream Weaver → Event-driven lifecycle with state transitions
The API unifies these into a single, subscription-aware GraphQL gateway.
### Core Principles
1. Subscription-first — Every mutation that changes observable state emits to subscribers
2. Datasource-agnostic — Resolvers talk to abstract datasource interfaces (Supabase, REST, in-memory)
3. Layered middleware — Auth, rate limiting, caching, and logging compose cleanly
4. Type-safe end-to-end — Schema → codegen → resolvers → client, all TypeScript
---
2. High-Level Architecture
┌──────────────────────────────────────┐
│ Client Layer │
│ (React, Mobile, CLI, Other APIs) │
└────────────┬─────────────────────────┘
│
┌────────────▼─────────────────────────┐
│ Transport Layer │
│ │
│ HTTP (queries/mutations) │
│ WebSocket (subscriptions via ws) │
│ SSE (alternative subscription │
│ transport for HTTP/2) │
└────────────┬─────────────────────────┘
│
┌────────────▼─────────────────────────┐
│ GraphQL Engine (Yoga) │
│ │
│ ┌─────────┐ ┌──────────┐ ┌────────┐ │
│ │ Schema │ │ Resolvers│ │ Plugins│ │
│ └────┬────┘ └────┬─────┘ └───┬────┘ │
│ │ │ │ │
│ ┌────▼───────────▼────────────▼────┐ │
│ │ Execution Pipeline │ │
│ │ Auth → Validate → Execute → Log │ │
│ └──────────────────────────────────┘ │
└────────────┬─────────────────────────┘
│
┌────────────▼─────────────────────────┐
│ Datasource Layer │
│ │
│ ┌──────────┐ ┌──────────┐ │
│ │ Supabase │ │ Redis │ │
│ │ (CRUD + │ │ (PubSub +│ │
│ │ Realtime│ │ Cache) │ │
│ └──────────┘ └──────────┘ │
│ ┌──────────┐ ┌──────────┐ │
│ │ REST │ │ In-Memory│ │
│ │ APIs │ │ (Dev) │ │
│ └──────────┘ └──────────┘ │
└──────────────────────────────────────┘---
3. Schema Design
Domain Modeling
The schema follows a domain-driven approach. Core domains identified from cross-project patterns:
# Core entity types (see full schema in src/schema/)
type User # Auth & identity
type Project # Container for work
type Dream # Dream lifecycle entity (from dream-weaver)
type Metric # Performance metrics (from performance-prophet)
type Thought # Distributed thoughts (from thought-mesh)
type Event # Generic domain event
type Notification # Push/stream notificationSchema Organization
src/schema/
├── base.graphql # Scalars, enums, interfaces, directives
├── user.graphql # User type, auth mutations
├── project.graphql # Project CRUD
├── dream.graphql # Dream lifecycle
├── metric.graphql # Metrics recording & querying
├── notification.graphql # Notification subscriptions
└── index.ts # Schema stitching / mergeKey Design Decisions
1. Relay-style pagination — All list fields use `Connection` types with `edges` + `pageInfo`
2. Input types for mutations — Every mutation takes a single `input` argument
3. Subscription payloads — Subscriptions return wrapper types with `event` enum + `data` union
4. Error unions — Mutations return `Result` union types (`Success | ValidationError | NotFoundError`)
---
4. Subscription Model
Transport: graphql-ws protocol
Using the modern `graphql-ws` protocol (not legacy `subscriptions-transport-ws`).
PubSub Architecture
┌──────────────┐
│ Mutation │
│ Resolver │
└──────┬───────┘
│ publish(topic, payload)
▼
┌──────────────┐
│ PubSub │
│ Engine │──────── Redis (production)
│ │──────── In-Memory (dev)
└──────┬───────┘
│ asyncIterator(topic)
▼
┌──────────────┐
│ Subscription │
│ Resolver │
│ (filter + │
│ transform) │
└──────┬───────┘
│
▼
┌──────────────┐
│ Client │
│ (WebSocket) │
└──────────────┘Topic Naming Convention
ENTITY.EVENT.SCOPE
Examples:
dream.updated.project:abc123
metric.recorded.system:global
notification.created.user:user456
thought.resonated.session:sess789Subscription Filtering
Subscriptions accept filter arguments to scope events:
subscription OnDreamUpdated($projectId: ID!) {
dreamUpdated(projectId: $projectId) {
event # CREATED | UPDATED | EVOLVED | BLOOMED
dream {
id
title
strength
stage
}
}
}Server-side filtering prevents sending irrelevant events to clients:
subscribe: {
resolve: (payload) => payload,
subscribe: withFilter(
() => pubsub.asyncIterableIterator('dream.updated'),
(payload, variables) => payload.projectId === variables.projectId
)
}---
5. Resolver Patterns
Pattern: Datasource Delegation
Resolvers are thin — they validate args, call datasources, publish events. No business logic in resolvers.
// Pattern: mutation resolver
const Mutation = {
createDream: async (_parent, { input }, ctx) => {
// 1. Auth check (middleware handles this via directive)
// 2. Delegate to datasource
const dream = await ctx.dataSources.dreams.create(input);
// 3. Publish event
ctx.pubsub.publish(`dream.created.project:${dream.projectId}`, {
event: 'CREATED',
dream,
});
// 4. Return result
return { __typename: 'CreateDreamSuccess', dream };
},
};Pattern: DataLoader for N+1
All relationship resolvers use DataLoader:
const Dream = {
project: (dream, _args, ctx) =>
ctx.loaders.projects.load(dream.projectId),
metrics: (dream, args, ctx) =>
ctx.loaders.dreamMetrics.load({ dreamId: dream.id, ...args }),
};Pattern: Subscription with Backpressure
For high-throughput subscriptions (metrics streaming), use buffered delivery:
subscribe: {
subscribe: pipe(
pubsub.asyncIterableIterator('metric.recorded'),
buffer(100, 1000), // max 100 items or 1s window
map(batch => ({ metricsBatch: batch }))
)
}---
6. Middleware Stack
Request → Auth → Rate Limit → Depth Limit → Complexity → Execute → Log → Response| Middleware | Purpose | Config |
|---|---|---|
| Auth | JWT validation, context enrichment | `@auth` directive |
| Rate Limit | Per-user/IP request throttling | 100 req/min default |
| Depth Limit | Prevent deeply nested queries | Max depth: 10 |
| Complexity | Query cost analysis | Max complexity: 1000 |
| Logging | Structured request/response logging | JSON format |
| Caching | Response caching for queries | Redis, TTL-based |
| Error Masking | Hide internal errors in production | env-based |
---
7. Authentication & Authorization
### Auth Flow
1. Client sends JWT in `Authorization: Bearer <token>` header
2. Auth middleware validates token, attaches user to context
3. `@auth` directive on fields checks role/permission
4. Subscriptions authenticate on `connection_init` message
Directive-Based Authorization
directive @auth(requires: Role = USER) on FIELD_DEFINITION
enum Role {
ADMIN
USER
VIEWER
}---
8. Supabase Integration
Supabase serves as the primary datasource with these integration points:
| Supabase Feature | GraphQL Usage |
|---|---|
| Postgres | Primary data store, queried via datasources |
| Auth | JWT validation, user management |
| Realtime | Database change events → PubSub bridge |
| Storage | File upload mutations |
| Edge Functions | Complex business logic delegation |
Realtime Bridge
Supabase Realtime changes are bridged into the GraphQL PubSub:
// Bridge Supabase Realtime → GraphQL PubSub
supabase
.channel('db-changes')
.on('postgres_changes', { event: '*', schema: 'public' }, (payload) => {
const topic = `${payload.table}.${payload.eventType}`;
pubsub.publish(topic, payload);
})
.subscribe();---
9. Error Handling
Result Union Pattern
union CreateDreamResult = CreateDreamSuccess | ValidationError | NotFoundError
type CreateDreamSuccess {
dream: Dream!
}
type ValidationError {
field: String!
message: String!
}
type NotFoundError {
entityType: String!
id: ID!
}Clients use inline fragments:
mutation {
createDream(input: { ... }) {
... on CreateDreamSuccess { dream { id title } }
... on ValidationError { field message }
}
}---
10. Performance Considerations
- DataLoader batches all N+1 queries within a single request tick
- Persisted queries reduce parsing overhead and prevent arbitrary queries
- Response caching with cache-control hints (`@cacheControl(maxAge: 60)`)
- Subscription throttling prevents overwhelming clients with rapid updates
- Query complexity analysis rejects expensive queries before execution
- Connection pooling for database connections via Supabase client
---
11. Technology Stack
| Component | Choice | Rationale |
|---|---|---|
| Server | GraphQL Yoga | Spec-compliant, plugin system, built-in ws |
| Schema | graphql-tools (SDL-first) | Readable, familiar, good tooling |
| Codegen | graphql-codegen | Type-safe resolvers + client types |
| PubSub | graphql-subscriptions + Redis | Scalable, multi-instance support |
| Auth | Supabase Auth + custom JWT | Existing integration pattern |
| ORM | Supabase JS client (no ORM) | Direct, less abstraction |
| Testing | Vitest | Consistent with existing projects |
| Runtime | Node.js / Bun | TypeScript native |
Promotion Decision
Promote into a technical note or architecture paper with implementation anchors.
Source Anchor
projects/dream-metamorphosis/graphql-api/docs/ARCHITECTURE.md
Detected Structure
Method · Code Anchors · Architecture