π 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¶
Error Response¶
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.