Skip to main content

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

  1. No timeline savings - Minimal API can be built in same 4-week timeline
  2. Migration debt - Would require large refactoring effort in MVP.1
  3. Mobile apps blocked - V2.0 mobile apps cannot use Supabase directly
  4. Testing complexity - Frontend tests require database access
  5. 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-key header

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

RiskImpactProbabilityMitigation
Migration takes longer than 4 weeksHIGHMEDIUMPre-build API endpoints during MVP.0 Weeks 8-10 (parallel work)
Frontend tightly coupled to SupabaseMEDIUMHIGHUse data access layer pattern (wrap Supabase calls in functions)
RLS policy bugs exposed lateMEDIUMLOWThorough RLS testing in MVP.0 (multi-tenant isolation tests)
API performance worse than direct SupabaseLOWLOWFastify is 3x faster than Express; Redis caching if needed
Users resist change during migrationLOWLOWMVP.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/ssr from frontend)

Temporarily Violated (Restored in MVP.1):

Fully Compliant (No Violations):

Supplementary:


References


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