ADR-0023: REST over GraphQL
Status
Accepted - 2025-01-26
Context
TVL Platform needs an API pattern for client-server communication with external partners (Hostaway, Airbnb, VRBO).
Decision
REST (Representational State Transfer) over GraphQL.
Rationale
- Industry Standard: 92% of APIs use REST (2024 survey)
- Caching: HTTP caching (GET, ETags) works out-of-box
- Partner Compatibility: All PMS/channel partners use REST
- Simplicity: Easier to learn and debug than GraphQL
- Tooling: Better HTTP tooling (curl, Postman)
Alternatives Considered
Alternative 1: GraphQL
Rejected - Overkill for CRUD, partners don't support it, complex caching
Alternative 2: gRPC
Rejected - Binary protocol (hard to debug), not suitable for web clients
Alternative 3: tRPC
Rejected - TypeScript-only (can't expose to partners), internal use only
REST Design Principles
1. Resource-Based URLs
GET    /api/v1/bookings           # List bookings
GET    /api/v1/bookings/:id       # Get booking
POST   /api/v1/bookings           # Create booking
PUT    /api/v1/bookings/:id       # Update booking (full)
PATCH  /api/v1/bookings/:id       # Update booking (partial)
DELETE /api/v1/bookings/:id       # Delete booking
2. HTTP Methods
| Method | Purpose | Idempotent | Safe | 
|---|---|---|---|
| GET | Retrieve resource | ✅ | ✅ | 
| POST | Create resource | ❌ | ❌ | 
| PUT | Replace resource | ✅ | ❌ | 
| PATCH | Update resource | ❌ | ❌ | 
| DELETE | Remove resource | ✅ | ❌ | 
3. HTTP Status Codes
200 OK - Success (GET, PUT, PATCH)
201 Created - Resource created (POST)
204 No Content - Success, no response body (DELETE)
400 Bad Request - Validation error
401 Unauthorized - Missing/invalid auth token
403 Forbidden - Valid token, insufficient permissions
404 Not Found - Resource doesn't exist
409 Conflict - Resource conflict (e.g., double booking)
422 Unprocessable Entity - Validation error (detailed)
500 Internal Server Error - Server error
4. Response Format
{
  "booking": {
    "id": "550e8400-e29b-41d4-a716-446655440000",
    "guestName": "John Doe",
    "checkIn": "2025-02-01T15:00:00Z",
    "checkOut": "2025-02-05T11:00:00Z",
    "totalCents": 50000,
    "status": "confirmed",
    "createdAt": "2025-01-26T12:00:00Z",
    "updatedAt": "2025-01-26T12:00:00Z"
  }
}