This document describes the API endpoints and database queries used in the GymFlow system. The application uses Supabase's auto-generated REST API.
All API requests go through Supabase's REST API:
Base URL: https://your-project-id.supabase.co/rest/v1
Required headers:
apikey: your_supabase_anon_key
Authorization: Bearer your_supabase_anon_key
Content-Type: application/json
The application uses a custom authentication layer with Supabase's database. In production, integrate Supabase Auth or implement JWT-based authentication.
- Query users table by email
- Verify password hash (implement bcrypt in production)
- Store user session in sessionStorage
- Include user role for authorization
GET /members?select=*,users:user_id(email,first_name,last_name,phone)
Response:
[
{
"id": "uuid",
"user_id": "uuid",
"status": "Active",
"join_date": "2024-01-15",
"emergency_contact_name": "Jane Doe",
"emergency_contact_phone": "+1-555-0199",
"users": {
"email": "john@example.com",
"first_name": "John",
"last_name": "Doe",
"phone": "+1-555-0100"
}
}
]GET /members?select=*,users:user_id(*),member_memberships(membership_plans(*))&id=eq.{member_id}
POST /members
Body:
{
"user_id": "uuid",
"status": "Active",
"join_date": "2024-01-15",
"emergency_contact_name": "Jane Doe",
"emergency_contact_phone": "+1-555-0199"
}
PATCH /members?id=eq.{member_id}
Body:
{
"status": "Frozen"
}
GET /trainers?select=*,users:user_id(email,first_name,last_name,phone)
Response:
[
{
"id": "uuid",
"user_id": "uuid",
"bio": "Certified trainer with 5 years experience",
"certifications": "ACE CPT, NASM",
"availability": {"monday": "9am-5pm"},
"users": {
"email": "trainer@gym.com",
"first_name": "Alex",
"last_name": "Smith",
"phone": "+1-555-0200"
}
}
]GET /trainers?select=*,users:user_id(*),trainer_members(members(users(*)))&id=eq.{trainer_id}
GET /membership_plans?select=*&is_active=eq.true
Response:
[
{
"id": "uuid",
"name": "Monthly Basic",
"duration_days": 30,
"price": "49.99",
"benefits": "Access to gym facilities, locker room",
"is_active": true
}
]POST /membership_plans
Body:
{
"name": "Quarterly Premium",
"duration_days": 90,
"price": 129.99,
"benefits": "All facilities, 2 PT sessions/month",
"is_active": true
}
GET /member_memberships?select=*,members(users(*)),membership_plans(*)&is_active=eq.true
POST /member_memberships
Body:
{
"member_id": "uuid",
"plan_id": "uuid",
"start_date": "2024-01-15",
"end_date": "2024-02-15",
"is_active": true
}
GET /attendance?select=*,members(users(*)),trainers(users(*))&order=checkin_time.desc&limit=100
Response:
[
{
"id": "uuid",
"member_id": "uuid",
"trainer_id": "uuid",
"checkin_time": "2024-01-15T09:30:00Z",
"checkout_time": "2024-01-15T11:00:00Z",
"members": {
"users": {
"first_name": "John",
"last_name": "Doe"
}
}
}
]POST /attendance
Body:
{
"member_id": "uuid",
"trainer_id": "uuid", // optional
"checkin_time": "2024-01-15T09:30:00Z"
}
PATCH /attendance?id=eq.{attendance_id}
Body:
{
"checkout_time": "2024-01-15T11:00:00Z"
}
GET /invoices?select=*,members(users(*)),membership_plans(*)&order=due_date.desc
Response:
[
{
"id": "uuid",
"member_id": "uuid",
"plan_id": "uuid",
"amount": "49.99",
"due_date": "2024-02-01",
"status": "due",
"created_at": "2024-01-15T00:00:00Z",
"members": {
"users": {
"first_name": "John",
"last_name": "Doe"
}
}
}
]POST /invoices
Body:
{
"member_id": "uuid",
"plan_id": "uuid",
"amount": 49.99,
"due_date": "2024-02-01",
"status": "due"
}
PATCH /invoices?id=eq.{invoice_id}
Body:
{
"status": "paid"
}
GET /payments?select=*,invoices(*)&order=paid_at.desc
POST /payments
Body:
{
"invoice_id": "uuid",
"amount": 49.99,
"method": "Credit Card",
"paid_at": "2024-01-15T10:00:00Z",
"reference": "TXN123456"
}
GET /equipment?select=*&order=name.asc
Response:
[
{
"id": "uuid",
"name": "Treadmill 1",
"purchase_date": "2023-01-15",
"condition": "Good",
"next_maintenance_date": "2024-02-15"
}
]POST /equipment
Body:
{
"name": "Treadmill 3",
"purchase_date": "2024-01-15",
"condition": "Excellent",
"next_maintenance_date": "2024-07-15"
}
GET /maintenance_logs?select=*,equipment(*)&order=performed_on.desc
POST /maintenance_logs
Body:
{
"equipment_id": "uuid",
"performed_on": "2024-01-15",
"notes": "Cleaned, lubricated, tested functionality"
}
GET /classes?select=*,trainers(users(*))&start_time=gte.{current_timestamp}&order=start_time.asc
Response:
[
{
"id": "uuid",
"title": "Yoga Flow",
"description": "Relaxing yoga session",
"capacity": 20,
"start_time": "2024-01-20T09:00:00Z",
"end_time": "2024-01-20T10:00:00Z",
"trainers": {
"users": {
"first_name": "Alex",
"last_name": "Smith"
}
}
}
]POST /classes
Body:
{
"title": "HIIT Training",
"description": "High-intensity interval training",
"capacity": 15,
"start_time": "2024-01-20T10:00:00Z",
"end_time": "2024-01-20T11:00:00Z",
"trainer_id": "uuid"
}
GET /class_bookings?select=*,members(users(*))&class_id=eq.{class_id}&status=eq.booked
POST /class_bookings
Body:
{
"class_id": "uuid",
"member_id": "uuid",
"status": "booked"
}
PATCH /class_bookings?class_id=eq.{class_id}&member_id=eq.{member_id}
Body:
{
"status": "cancelled"
}
SELECT COUNT(*) as active_members
FROM members
WHERE status = 'Active'
AND id IN (
SELECT member_id
FROM member_memberships
WHERE is_active = true
);SELECT SUM(
mp.price * 30.0 / mp.duration_days
) as mrr
FROM membership_plans mp
JOIN member_memberships mm ON mp.id = mm.plan_id
WHERE mm.is_active = true;SELECT COUNT(*) as overdue_count
FROM invoices
WHERE status = 'overdue'
OR (status = 'due' AND due_date < CURRENT_DATE);SELECT COUNT(*) as today_checkins
FROM attendance
WHERE DATE(checkin_time) = CURRENT_DATE;SELECT COUNT(*) as maintenance_due
FROM equipment
WHERE next_maintenance_date <= CURRENT_DATE + INTERVAL '30 days';SELECT
DATE(checkin_time) as date,
COUNT(*) as checkins
FROM attendance
WHERE checkin_time >= CURRENT_DATE - INTERVAL '7 days'
GROUP BY DATE(checkin_time)
ORDER BY date;SELECT
TO_CHAR(created_at, 'Mon YYYY') as month,
SUM(amount) as revenue
FROM invoices
WHERE status = 'paid'
GROUP BY TO_CHAR(created_at, 'Mon YYYY')
ORDER BY MIN(created_at) DESC
LIMIT 6;Supabase supports PostgREST query parameters:
# Equal to
GET /members?status=eq.Active
# Not equal
GET /members?status=neq.Inactive
# Greater than
GET /attendance?checkin_time=gte.2024-01-01
# Pattern matching
GET /users?email=like.*@gmail.com
# Multiple conditions
GET /members?status=eq.Active&join_date=gte.2024-01-01
# Ascending
GET /members?order=join_date.asc
# Descending
GET /invoices?order=due_date.desc
# Multiple columns
GET /members?order=status.asc,join_date.desc
# Limit results
GET /members?limit=10
# Offset and limit
GET /members?offset=10&limit=10
# Range-based pagination
Headers: Range: 0-9
200 OK- Successful request201 Created- Resource created successfully400 Bad Request- Invalid request parameters401 Unauthorized- Missing or invalid authentication403 Forbidden- Insufficient permissions (RLS)404 Not Found- Resource not found409 Conflict- Duplicate resource or constraint violation500 Internal Server Error- Server error
{
"message": "Error description",
"details": "Additional details",
"hint": "Suggestion to fix the issue",
"code": "ERROR_CODE"
}Supabase has built-in rate limiting:
- Free tier: 500 requests per second
- Pro tier: 1000+ requests per second
Implement client-side throttling and caching for better performance.
-
Row Level Security: All tables have RLS enabled. Users can only access data they're authorized to see.
-
API Keys: Never expose the service role key in client-side code. Only use the anon key.
-
Input Validation: Always validate and sanitize user input before sending to the API.
-
HTTPS Only: All API requests must use HTTPS in production.
-
Use Specific Selects: Only query the columns you need
GET /members?select=id,status,users(first_name,last_name) -
Implement Caching: Cache frequently accessed, rarely changing data
-
Batch Operations: Use Supabase's bulk insert when creating multiple records
-
Indexes: The schema includes indexes on commonly queried fields
-
Connection Pooling: Supabase handles connection pooling automatically
For Supabase-specific API documentation: