ADR-0053: Temporary Supabase Direct Access for MVP.0
Status
Rejected - 2025-01-26
Supersedes: None Superseded By: ADR-0054: API-First Approach for MVP.0
Decision
This ADR was rejected during planning. We chose to implement a minimal Fastify API from day 1 (ADR-0054) instead of using Supabase direct access.
Why Rejected
- No timeline savings - Minimal API can be built in same 4-week timeline
- Migration debt - Would require large refactoring effort in MVP.1
- Mobile apps blocked - V2.0 mobile apps cannot use Supabase directly
- Testing complexity - Frontend tests require database access
- ADR violations - Temporary violations create confusion and technical debt
See: ADR-0054: API-First Approach for MVP.0 for accepted approach.
Context
TVL Platform has established a comprehensive API architecture through ADRs 0022-0026:
- ADR-0022: Fastify API Framework
- ADR-0023: REST API Pattern
- ADR-0024: OpenAPI Specification
- ADR-0025: RFC 7807 Error Handling
- ADR-0026: URL-Based API Versioning
These ADRs define the target architecture for a production-grade API layer that enables:
- Mobile apps and external partners to integrate
- Consistent error handling and versioning
- OpenAPI documentation for client SDK generation
However, implementing this full API layer adds 2-3 weeks to the MVP.0 timeline (Weeks 1-10). MVP.0's core objective is to validate product-market fit by demonstrating one-way sync (TVL → Hostaway) for property managers.
The Question: Should MVP.0 build the full Fastify API layer, or use Supabase direct access to ship faster?
Decision
For MVP.0 (Weeks 1-10): Use Supabase direct access from Next.js frontend (temporary ADR violation).
For MVP.1+ (Weeks 11-16): Migrate to Fastify REST API (restore full ADR compliance).
Rationale
Why Temporary Violation is Acceptable
1. Product Validation Priority
- MVP.0 is about validating demand ("Will property managers use this?")
- Shipping 3 weeks earlier means faster customer feedback
- If product fails validation, architectural perfection is wasted effort
2. Limited External Exposure
- MVP.0 has no external API clients (no mobile apps, no partner integrations)
- Internal admin UI only (3-5 users)
- Supabase RLS policies enforce multi-tenancy isolation
3. Clear Migration Path
- MVP.1 adds two-way sync (Hostaway → TVL), which requires API layer for webhooks
- Migration happens before external clients exist
- Timeline explicitly allocates migration effort (MVP.1 Weeks 11-16)
4. Reduced Risk Surface
- Fewer moving parts in MVP.0 (no Fastify, no OpenAPI, no RFC 7807 middleware)
- Faster iteration on UI/UX based on user feedback
- Database schema remains ADR-compliant (multi-tenancy, RLS, versioning)
Why Migration is Mandatory
MVP.1 Requirements Demand API Layer:
- Two-way sync requires Hostaway webhooks → TVL API endpoints
- Booking ingestion requires API endpoints for external events
- Mobile app (V2.0+) requires REST API (can't call Supabase directly)
Technical Debt Cap:
- Migration happens before external clients exist (zero breaking changes)
- 4-week migration window (MVP.1 Weeks 11-14 for API, Weeks 15-16 for frontend)
- Clear success criteria (all frontend calls go through API, Supabase inaccessible from frontend)
Violated ADRs (Temporary)
ADR-0022: Fastify API Framework
Violation: Next.js calls Supabase directly instead of Fastify API. Restored In: MVP.1 Week 11-12 (Fastify API implementation)
ADR-0023: REST API Pattern
Violation: No REST endpoints (direct database queries from frontend). Restored In: MVP.1 Week 11-12 (REST endpoints for all resources)
ADR-0024: OpenAPI Specification
Violation: No OpenAPI spec (no API to document). Restored In: MVP.1 Week 13 (OpenAPI spec auto-generated from Fastify schemas)
ADR-0025: RFC 7807 Error Handling
Violation: No standardized error format (Supabase errors returned directly). Restored In: MVP.1 Week 12 (RFC 7807 middleware in Fastify)
ADR-0026: URL-Based API Versioning
Violation: No versioned API endpoints.
Restored In: MVP.1 Week 11 (Start with /api/v1/ prefix)
MVP.0 Architecture (Weeks 1-10)
┌─────────────────────────────────────────────────────────────────┐
│                          Next.js 14 Frontend                     │
│                      (Server Components + Client)                │
└──────────────────────────────┬──────────────────────────────────┘
                               │
                               │ Direct Supabase Client
                               │ (createClient from @supabase/ssr)
                               │
                               ▼
                    ┌──────────────────────┐
                    │   Supabase Database  │
                    │   (PostgreSQL 15+)   │
                    │   - RLS Policies     │
                    │   - org_id filtering │
                    └──────────────────────┘
Code Pattern (MVP.0)
// app/properties/page.tsx (Server Component)
import { createClient } from '@/lib/supabase/server'
import { cookies } from 'next/headers'
export default async function PropertiesPage() {
  const supabase = createClient(cookies())
  // Direct Supabase query (RLS enforces org_id isolation)
  const { data: properties, error } = await supabase
    .from('spaces')
    .select('*')
    .order('created_at', { ascending: false })
  if (error) throw error
  return <PropertyList properties={properties} />
}
Security: Multi-tenancy enforced by RLS policies (ADR-0001, ADR-0002) - no violations here.
MVP.1+ Architecture (Weeks 11-16)
┌─────────────────────────────────────────────────────────────────┐
│                          Next.js 14 Frontend                     │
│                      (Server Components + Client)                │
└──────────────────────────────┬──────────────────────────────────┘
                               │
                               │ fetch('/api/v1/properties')
                               │ (REST API calls only)
                               │
                               ▼
                    ┌──────────────────────┐
                    │   Fastify API (4.x)  │
                    │   - OpenAPI 3.0      │
                    │   - RFC 7807 errors  │
                    │   - /api/v1/* routes │
                    └──────────┬───────────┘
                               │
                               │ Drizzle ORM
                               │
                               ▼
                    ┌──────────────────────┐
                    │   Supabase Database  │
                    │   (PostgreSQL 15+)   │
                    └──────────────────────┘
Code Pattern (MVP.1+)
// app/properties/page.tsx (Server Component)
export default async function PropertiesPage() {
  // Fetch from Fastify API instead of Supabase
  const response = await fetch(`${process.env.API_URL}/api/v1/properties`, {
    headers: {
      'Authorization': `Bearer ${getAccessToken()}`,
      'Content-Type': 'application/json'
    },
    cache: 'no-store'
  })
  if (!response.ok) {
    const problem = await response.json() // RFC 7807 format
    throw new Error(problem.detail)
  }
  const { properties } = await response.json()
  return <PropertyList properties={properties} />
}
Restored Compliance: All ADRs 0022-0026 fully implemented.
Migration Plan (MVP.1 Weeks 11-16)
Phase 1: API Backbone (Weeks 11-12)
Week 11: Fastify Setup
-  Install Fastify 4.x + plugins (@fastify/swagger,@fastify/cors,@fastify/jwt)
-  Create /services/api/workspace (pnpm monorepo)
- Configure OpenAPI 3.0 spec generation
- Setup RFC 7807 error handler middleware
- Configure JWT authentication (Supabase Auth tokens)
-  Create health check endpoint (GET /health)
Week 12: Core Endpoints
-  GET /api/v1/properties- List spaces (with RLS filtering)
-  GET /api/v1/properties/:id- Get space
-  POST /api/v1/properties- Create space
-  PUT /api/v1/properties/:id- Update space
-  DELETE /api/v1/properties/:id- Delete space
- Repeat for units, channel targets, listings
- Integration tests for all endpoints
Phase 2: Frontend Migration (Weeks 13-14)
Week 13: Data Fetching Layer
-  Create /lib/api/client.ts- Fetch wrapper with error handling
- Replace Supabase calls in Server Components (properties, units)
-  Update TanStack Query hooks (useProperties,useUnits)
- Test multi-tenancy isolation (ensure org_id passed correctly)
Week 14: Forms & Mutations
- Replace Supabase mutations with API calls (create, update, delete)
- Update React Hook Form submission handlers
- Test error handling (RFC 7807 errors displayed correctly)
- Remove Supabase client from frontend bundle (security)
Phase 3: Webhook Infrastructure (Weeks 15-16)
Week 15: Hostaway Webhooks
-  POST /api/v1/webhooks/hostaway/bookings- Booking events
-  POST /api/v1/webhooks/hostaway/availability- Availability updates
- Webhook signature verification (HMAC-SHA256)
-  Idempotency via x-idempotency-keyheader
Week 16: Testing & Validation
- End-to-end tests (frontend → API → database)
- Load testing (100 req/sec)
- Security audit (no frontend database access)
- Documentation update (OpenAPI spec published)
Success Criteria
MVP.0 Completion (Week 10):
- ✅ Frontend calls Supabase directly
- ✅ RLS policies enforce multi-tenancy
- ✅ One-way sync (TVL → Hostaway) working
- ✅ 10+ properties syncing successfully
MVP.1 Migration Complete (Week 16):
- ✅ All frontend calls go through Fastify API (/api/v1/*)
- ✅ Zero direct Supabase calls from Next.js
- ✅ OpenAPI spec auto-generated and published
- ✅ RFC 7807 errors returned consistently
- ✅ Webhooks receiving Hostaway events
- ✅ Two-way sync (TVL ↔ Hostaway) working
Risks & Mitigation
| Risk | Impact | Probability | Mitigation | 
|---|---|---|---|
| Migration takes longer than 4 weeks | HIGH | MEDIUM | Pre-build API endpoints during MVP.0 Weeks 8-10 (parallel work) | 
| Frontend tightly coupled to Supabase | MEDIUM | HIGH | Use data access layer pattern (wrap Supabase calls in functions) | 
| RLS policy bugs exposed late | MEDIUM | LOW | Thorough RLS testing in MVP.0 (multi-tenant isolation tests) | 
| API performance worse than direct Supabase | LOW | LOW | Fastify is 3x faster than Express; Redis caching if needed | 
| Users resist change during migration | LOW | LOW | MVP.0 has 3-5 internal users only (no external clients) | 
Enforcement
MVP.0 (Current Phase)
- ✅ Allowed: Direct Supabase calls from Next.js (Server Components and Client Components)
- ✅ Required: RLS policies on all tables (multi-tenancy isolation)
- ✅ Required: Data access layer pattern (functions, not inline queries)
- ❌ Forbidden: Exposing Supabase credentials to external clients
MVP.1+ (Target State)
- ✅ Required: All data access via Fastify API (/api/v1/*)
- ✅ Required: OpenAPI spec auto-generated and published
- ✅ Required: RFC 7807 error responses
- ❌ Forbidden: Direct Supabase calls from Next.js (remove @supabase/ssrfrom frontend)
Related ADRs
Temporarily Violated (Restored in MVP.1):
- ADR-0022: Fastify API Framework
- ADR-0023: REST API Pattern
- ADR-0024: OpenAPI Specification
- ADR-0025: RFC 7807 Error Handling
- ADR-0026: URL-Based API Versioning
Fully Compliant (No Violations):
- ADR-0001: Multi-Tenancy Architecture - RLS policies enforced
- ADR-0002: PostgreSQL Data Modeling - Schema follows standards
- ADR-0015: React 18 - Frontend framework
- ADR-0016: Next.js 14 App Router - SSR framework
Supplementary:
- ADR-0052: Docusaurus + Cloudflare Pages - Documentation hosting
References
- MVP.0 Specification
- MVP.1 Specification
- v0-project-spec.md - Frontend implementation guide
- Supabase Documentation
- Fastify Documentation
- The Pragmatic Programmer: Technical Debt
Last Updated: 2025-01-26 Review Date: 2025-03-15 (End of MVP.0, before MVP.1 migration starts) Status: Active - Migration plan to be executed in MVP.1