Skip to content

πŸ”Œ API Design (MVP)

Overview

Simple REST API using Cloudflare Workers with SvelteKit as the framework.

API Structure

Base URLs

  • Development: http://localhost:5173/api
  • Staging: https://staging.getcimple.com/api
  • Production: https://app.getcimple.com/api

Authentication

All API requests require authentication cookie from Kinde.

Core Endpoints

Organizations

GET    /api/organizations          # List user's orgs
GET    /api/organizations/:id      # Get org details
POST   /api/organizations/:id/switch # Switch active org

Essential Eight Assessments

GET    /api/e8/current            # Current assessment
POST   /api/e8/assessments        # Create assessment
PUT    /api/e8/assessments/:id    # Update assessment
GET    /api/e8/history            # Assessment history

Policies

GET    /api/policies              # List policies
GET    /api/policies/:id          # Get policy
POST   /api/policies              # Create policy
PUT    /api/policies/:id          # Update policy
POST   /api/policies/:id/approve  # Approve (directors)

Tasks

GET    /api/tasks                 # List tasks
GET    /api/tasks/:id             # Get task
POST   /api/tasks                 # Create task
PUT    /api/tasks/:id             # Update task
POST   /api/tasks/:id/complete    # Mark complete

Reports

GET    /api/reports/board         # Generate board report
GET    /api/reports/compliance    # Compliance summary
GET    /api/reports/export/:type  # Export as PDF/Excel

Request/Response Format

Standard Response

{
  "data": { ... },
  "meta": {
    "page": 1,
    "total": 42
  }
}

Error Response

{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "You don't have access to this resource"
  }
}

Common Status Codes

  • 200: Success
  • 201: Created
  • 400: Bad Request
  • 401: Unauthorized
  • 403: Forbidden
  • 404: Not Found
  • 500: Server Error

Implementation Pattern

SvelteKit API Route

// +server.ts
import { json } from '@sveltejs/kit'
import { db } from '$lib/server/db'

export async function GET({ locals, params }) {
  // Check auth
  if (!locals.user) {
    return json({ error: 'Unauthorized' }, { status: 401 })
  }

  // Get data
  const data = await db
    .from('policies')
    .select('*')
    .eq('organization_id', locals.organization.id)

  return json({ data })
}

export async function POST({ request, locals }) {
  const body = await request.json()

  // Validate
  if (!body.title) {
    return json({ error: 'Title required' }, { status: 400 })
  }

  // Create
  const policy = await db
    .from('policies')
    .insert({
      ...body,
      organization_id: locals.organization.id,
      created_by: locals.user.id,
    })
    .select()
    .single()

  return json({ data: policy }, { status: 201 })
}

What We're NOT Doing (Yet)

  • GraphQL (REST is simpler)
  • Versioning (v1 implied)
  • Complex filtering (basic only)
  • Batch operations
  • Webhooks
  • Rate limiting per user (Cloudflare handles)
  • API keys (session auth only)

Testing APIs

Local Testing

# With httpie
http :5173/api/policies Cookie:session=...

# With curl
curl http://localhost:5173/api/policies \
  -H "Cookie: session=..."

Integration Tests

// api.test.ts
test('GET /api/policies returns policies', async () => {
  const response = await app.request('/api/policies', {
    headers: { cookie: 'session=test' },
  })

  expect(response.status).toBe(200)
  const data = await response.json()
  expect(data).toHaveProperty('data')
})

Keep APIs simple. Use REST. Standard patterns. Focus on core features.