Skip to content

Architecture Overview

!!! info "TL;DR" Next.js 15 + Supabase + Vercel. App Router with Server Actions. PostgreSQL with RLS. Realtime subscriptions. Modular monolith with domain prefixes.

High-Level Architecture

Canviq is a full-stack Next.js application deployed on Vercel with Supabase for backend services. The architecture follows a modular monolith pattern with clear domain boundaries.

┌─────────────────────────────────────────────────────────┐
│                    Vercel Edge Network                  │
│                     (Global CDN)                        │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│                  Next.js 15 Application                 │
│  ┌──────────────────────────────────────────────────┐  │
│  │  App Router (src/app)                            │  │
│  │  ├── [locale]/                                   │  │
│  │  │   ├── Page Components (React 19)             │  │
│  │  │   ├── Server Actions (mutations)             │  │
│  │  │   └── Layout Components                      │  │
│  │  └── api/                                        │  │
│  │      └── Route Handlers (REST endpoints)        │  │
│  └──────────────────────────────────────────────────┘  │
│                                                          │
│  ┌──────────────────────────────────────────────────┐  │
│  │  Middleware                                      │  │
│  │  ├── Auth validation (HMAC session cookies)     │  │
│  │  ├── Locale routing (next-intl)                 │  │
│  │  └── Rate limiting                              │  │
│  └──────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│              Supabase (Backend Services)                │
│  ┌──────────────────────────────────────────────────┐  │
│  │  PostgreSQL 15                                   │  │
│  │  ├── Row-level security (RLS)                   │  │
│  │  ├── Full-text search (tsvector)                │  │
│  │  ├── Database triggers                          │  │
│  │  └── Materialized views                         │  │
│  └──────────────────────────────────────────────────┘  │
│                                                          │
│  ┌──────────────────────────────────────────────────┐  │
│  │  Realtime                                        │  │
│  │  ├── Postgres CDC                               │  │
│  │  └── WebSocket subscriptions                    │  │
│  └──────────────────────────────────────────────────┘  │
│                                                          │
│  ┌──────────────────────────────────────────────────┐  │
│  │  Auth                                            │  │
│  │  ├── User management                            │  │
│  │  ├── OAuth providers                            │  │
│  │  └── Session management                         │  │
│  └──────────────────────────────────────────────────┘  │
│                                                          │
│  ┌──────────────────────────────────────────────────┐  │
│  │  Storage                                         │  │
│  │  └── User avatars, attachments                  │  │
│  └──────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐
│              External Integrations                      │
│  ├── GitHub (issue creation)                           │
│  ├── Resend (email notifications)                      │
│  ├── Anthropic (sentiment analysis, NL parsing)        │
│  ├── Inngest (workflow automation)                     │
│  └── Upstash Redis (rate limiting)                     │
└─────────────────────────────────────────────────────────┘

Key Design Decisions

1. Next.js App Router

Uses React Server Components by default. Benefits:

  • Server-first rendering — Most components render on the server, reducing bundle size
  • Streaming — Progressive page rendering for faster perceived load times
  • Server Actions — Type-safe mutations without writing API routes
  • Automatic code splitting — Each route is a separate chunk

Client components are used only for interactivity (forms, realtime updates, animations).

2. Supabase for Backend

Supabase provides PostgreSQL, Auth, Realtime, and Storage in a unified platform. Benefits:

  • Managed infrastructure — No server maintenance
  • RLS for security — Row-level security policies enforce authorization at the database level
  • Realtime subscriptions — Live updates via PostgreSQL CDC
  • Built-in Auth — OAuth, email/password, MFA support
  • Self-hostable — Can migrate to self-hosted Supabase if needed

3. Standalone Deployment

This product is completely separate from the core Revoir website:

  • Separate Vercel project — Independent deployments
  • Separate Supabase project — Isolated database and auth
  • Separate codebase — No shared code with core site
  • Custom domaincanviq.app (not a subdomain of revoir.app)

This isolation allows independent evolution and easier extraction as a standalone product.

4. Modular Monolith

The codebase is organized by domain with prefixed tables:

  • Feedback domainsubmissions, votes, comments, follows
  • Survey domainsurvey_* tables (15+ tables)
  • Agent IAM domainagent_* tables
  • Workflow domainworkflow_* tables

Each domain has its own:

  • Database tables
  • API routes (api/submissions/, api/surveys/)
  • React components (components/submissions/, components/surveys/)
  • Business logic (lib/submissions.ts, lib/survey/)

This structure supports future extraction into microservices if needed, but avoids premature distribution complexity.

Data Flow

Read Path (Server Component)

  1. User requests page
  2. Next.js server component executes
  3. Component calls Supabase client (server variant)
  4. PostgreSQL query with RLS applied
  5. Data returned to component
  6. Component renders HTML
  7. HTML streamed to client

No client-side JavaScript needed for read-only pages.

Write Path (Server Action)

  1. User submits form
  2. Browser POSTs to Server Action endpoint
  3. Server Action validates input (Zod schema)
  4. Server Action calls Supabase client
  5. PostgreSQL mutation with RLS applied
  6. Database trigger updates denormalized counts
  7. Server Action returns result
  8. Client router refreshes page data

Realtime Path (Subscription)

  1. Client component subscribes to Realtime channel
  2. PostgreSQL CDC detects change
  3. Supabase Realtime broadcasts event
  4. Client receives event via WebSocket
  5. Client updates UI optimistically

Security Layers

1. Middleware

  • Validates HMAC-signed session cookies
  • Rejects forged or expired sessions
  • Sets x-user-id header for downstream use

2. Row-Level Security (RLS)

Every table has RLS enabled. Policies enforce:

  • Public read for submissions, categories, tags
  • Authenticated write for votes, comments
  • Admin-only access for moderation, analytics
  • Agent API key validation for MCP tools

3. Server Actions

  • Validate input with Zod schemas
  • Never trust client-provided user IDs
  • Re-authenticate using auth() helper
  • Log security-relevant actions to audit log

4. Rate Limiting

  • In-memory rate limiting for standard endpoints
  • Distributed rate limiting (Upstash Redis) for MCP agents
  • Per-user and per-IP limits

Deployment Pipeline

Git Push → GitHub
GitHub Actions
    ├── Lint (ESLint)
    ├── Type check (tsc)
    ├── Unit tests (Vitest)
    └── Build (next build)
Vercel
    ├── Build Next.js app
    ├── Deploy to CDN
    ├── Run migrations (Supabase CLI)
    └── Generate preview URL
E2E Tests (Playwright on preview)
Merge to main → Production deploy

Scalability Considerations

Current Scale (Launch)

  • Expected load: 1,000 daily active users
  • Peak: 100 concurrent users
  • Database: 100,000 submissions, 1M votes, 500K comments
  • Vercel serverless handles this easily

Growth Scale (Year 1)

  • Expected load: 10,000 daily active users
  • Peak: 1,000 concurrent users
  • Database: 1M submissions, 10M votes, 5M comments
  • May need Supabase Pro plan for connection pooling

Enterprise Scale (Year 2+)

  • Expected load: 100,000+ daily active users
  • Peak: 10,000+ concurrent users
  • Consider:
  • Read replicas for heavy queries
  • Edge caching for static content
  • Dedicated Supabase instance
  • Background job workers for heavy operations

The modular monolith architecture supports gradual extraction of domains into separate services as scale demands.

What's Next