Canvas Schema

Feature Owner: scorevi (Sean Patrick Caintic)
Module: Design
Priority: P0
Sprint #12: Fully Implemented
Date: 2026-06-29


EXECUTIVE SUMMARY

What is this feature?
A Zod-validated canvas metadata schema (canvasMetadataSchema) that defines the structure for React Flow visual canvas state, persisted in a Supabase JSONB column on the quests table. Includes bidirectional sync between the visual canvas and the form-based editor.

Why does it matter?
The visual canvas editor is the core differentiator for WyzQuests — creators design quest flows visually. Without a strict schema, the canvas state could become corrupted, causing the React Flow editor to crash or lose work.

What's the MVP scope?
Zod schemas (canvasNodeSchema, canvasEdgeSchema, canvasMetadataSchema) in shared/schemas/questSchema.ts. Database migration with JSONB column, CHECK constraint, and GIN index. Canvas-to-form and form-to-canvas sync utilities in lib/canvas-sync/. AI curriculum-to-canvas converter and deterministic layout engine.


1. USER PAIN POINT & SOLUTION

Current State (Without Feature)

Quest creators have no visual way to design branching quest paths. All quest structure is linear and form-driven, making complex branching learning paths impossible to visualize or author.

Pain Point

Emotional: Frustration and overwhelm when designing multi-path learning experiences through form fields alone.
Functional: Cannot visualize decision points, branching paths, or quest flow. Prone to logical errors in quest structure.
Business Impact: Limits the platform to simple linear quests, reducing appeal for sophisticated instructional designers.

Future State (With Feature)

Creators use a React Flow visual canvas with draggable nodes and connectable edges. The canvas state is validated at every save and bidirectional sync keeps the form editor and canvas consistent.

Marketing Hook

"Design quests the way learners experience them — visually."


2. 4D FRAMEWORK MAPPING

Diagnose

N/A — schema/infrastructure concern.

Design

Core enabler of the Design phase. Creators visually lay out quest flow, add branching logic, and connect nodes. The canvas schema validates every save.

Develop

The canvasToForm.ts utility extracts content cards from canvas nodes so the Develop phase (activity authoring) has structured data.

Deliver

Canvas metadata includes quest structure for SCORM/xAPI export — branching paths are preserved.


3. USER FLOWS

Entry Point

Creator opens the dual editor (Form + Canvas). The canvas loads existing canvas_metadata from the database.

Success Criteria

Nodes and edges are rendered in React Flow. Saving persists validated canvas state to Supabase. Switching between Form and Canvas views shows consistent data.

Main Flow (Happy Path)

  1. Creator enters the dual editor for a quest.

  2. React Flow renders nodes and edges from canvas_metadata.nodes and canvas_metadata.edges.

  3. Creator drags nodes, creates edges, adds branching logic.

  4. On save, canvasMetadataSchema validates the payload.

  5. Valid canvas state is stored in quests.canvas_metadata JSONB column.

Edge Cases

  • Empty canvas: Defaults to { nodes: [], edges: [], quest_mode: "exploration", title: "" }.

  • Invalid JSON in DB: CHECK constraint rejects writes where nodes or edges is not an array.

  • Zod validation failure: API returns ApiResponseHelper.validationError() with 400 status.

  • Linear mode: Canvas is disabled; only the form editor is active.

Decision Points

  • IF quest_mode === "linear" → Canvas view hidden, only form editor shown.

  • ELSE (quest_mode === "exploration") → Dual editor with Canvas enabled.


4. INFORMATION ARCHITECTURE

Primary Information (Always visible)

  • canvasMetadataSchema: { nodes: CanvasNode[], edges: CanvasEdge[], quest_mode: "linear" | "exploration", title: string }

  • CanvasNode: { id: string, x: number, y: number, type: string, data?: Record<string, unknown> }

  • CanvasEdge: { id?: string, source: string, target: string, sourceHandle?: string | null, targetHandle?: string | null, label?: string, logic?: string, edgeType?: string }

Secondary Information

  • Edge logic field for conditional branching.

  • Edge edgeType for differentiated edge styling.

Actions

Primary CTA: Save Canvas — persists to Supabase via PUT /api/creator/update-quest-canvas with updateCanvasSchema.
Secondary Actions: Reset Canvas, Import from AI Curriculum.


5. WIREFRAMES

[Excluded — existing UI]

6. WIREFLOWS

Excluded.

7. PROTOTYPE

[Excluded — existing implementation]


8. BACKEND SCHEMA

Database Tables

-- Migration: 001_add_canvas_metadata.sql
ALTER TABLE quests
ADD COLUMN IF NOT EXISTS canvas_metadata JSONB
NOT NULL
DEFAULT '{"nodes": [], "edges": []}'::jsonb;
 
-- CHECK constraint: nodes and edges must be arrays
ALTER TABLE quests
ADD CONSTRAINT canvas_metadata_structure_check
CHECK (
canvas_metadata IS NOT NULL
AND canvas_metadata ? 'nodes'
AND canvas_metadata ? 'edges'
AND jsonb_typeof(canvas_metadata->'nodes') = 'array'
AND jsonb_typeof(canvas_metadata->'edges') = 'array'
);
 
-- GIN index for JSONB queries
CREATE INDEX IF NOT EXISTS idx_quests_canvas_metadata
ON quests USING GIN (canvas_metadata);

Note: The quest_mode field is NOT in this migration — it is validated by Zod at the application layer.


9. API ENDPOINTS

Method

Endpoint

Auth

Schema

Response

PUT

/api/creator/update-quest-canvas

Creator

updateCanvasSchema ({ quest_id: UUID, canvas_metadata: canvasMetadataSchema })

ApiResponseHelper.success() / .validationError()


10. DATA REQUIREMENTS

Frontend Needs

  • CanvasMetadata type for React Flow state management.

  • CanvasNode and CanvasEdge types for node/edge components.

  • questModeSchema for toggling canvas visibility.

API Calls Frontend Will Make

  • PUT /api/creator/update-quest-canvas with { quest_id, canvas_metadata }.

Caching Strategy

Canvas state is persisted on explicit save. In-memory during editing via React Flow's internal state.


11. PERFORMANCE CONSIDERATIONS

Database Optimization

  • GIN index on canvas_metadata for future JSONB path queries.

  • CHECK constraint prevents invalid writes at DB level.

API Response Time

  • JSONB write is lightweight; typical payload is <100 nodes/edges.

  • No query-time overhead beyond standard Supabase row write.


12. SECURITY & AUTHORIZATION

Who can access this feature?

Quest creators with a quest in draft or in-progress status. RLS on quests table enforces creator_id ownership.

Authorization Logic

  • RLS: creator_id = auth.uid() on the quests table.

  • API route validates via authenticateRole('CREATOR') or authenticateAnyRole(['CREATOR', 'ADMIN']).

Data Validation

  • Zod canvasMetadataSchema validates on every API write.

  • updateCanvasSchema ensures quest_id is a valid UUID.

  • PostgreSQL CHECK constraint as defense-in-depth.


13. ERROR HANDLING

Error

Response

Invalid quest_id (not UUID)

400 ApiResponseHelper.validationError()

canvas_metadata fails Zod validation

400 ApiResponseHelper.validationError() with Zod error details

CHECK constraint violation

500 (should never reach DB if Zod passes)

Unauthorized (not quest owner)

403 RLS rejection or ApiResponseHelper.forbidden()


14. TESTING CHECKLIST

Happy Path
□ Create nodes and edges via React Flow, save, reload — state persists.
□ Switch between Form and Canvas views — data is consistent in both directions.
□ Empty canvas renders without errors (default empty arrays).
QUEST_MODE linear hides canvas; exploration shows it.

Edge Cases
□ Save malformed JSON (missing nodes array) — Zod rejects before DB write.
□ Save duplicate node IDs — handled by React Flow (id uniqueness enforced).
□ Very large canvas (>1000 nodes) — test render performance.
□ Concurrency: two tabs editing same canvas — last write wins.


15. OPEN QUESTIONS

  • Should the CHECK constraint be extended to validate node shape (id, x, y, type fields)?

  • Should quest_mode be added to the CHECK constraint or a separate DB column?


16. OUT OF SCOPE (v1.1+)

  • Real-time collaborative canvas editing (WebSocket / Supabase Realtime).

  • Canvas version history / undo beyond browser session.

  • Canvas node templates (pre-built branching patterns).


17. SUCCESS METRICS

  • Zero canvas data corruption events.

  • 100% of canvas saves validated by Zod before DB write.

  • Canvas-to-form sync produces no data loss.


18. DEPENDENCIES

This feature depends on:

  • React Flow (@xyflow/react) for canvas rendering.

  • Zod for schema validation.

  • Supabase JSONB for persistence.

  • lib/canvas-sync/canvasToForm.ts (259 lines) for canvas-to-card extraction.

  • lib/canvas-sync/formToCanvas.ts for form-to-canvas conversion.

  • lib/utils/curriculum-to-canvas.ts for AI curriculum import.

  • lib/ai/canvas-layout.service.ts (268 lines) for deterministic initial layout.

These features depend on this:

  • Visual Canvas Editor (React Flow integration).

  • AI Quest Generation (outputs to canvas).

  • SCORM/xAPI Export (reads canvas structure).


19. TIMELINE & OWNERSHIP

  • Implemented: W1.5 milestone.

  • Owner: scorevi


Document Version

1.0 - Initial version - 2026-06-29 08:27 UTC

1.1 - Added Document Version section and update author to have full name - 2026-06-29 08:52 UTC


Was this article helpful?