CHRIS uses Supabase's auto-generated REST API (PostgREST) for database operations.
Base URL
https://[PROJECT_REF].supabase.co/rest/v1
Authentication
All requests require authentication via JWT token.
Headers
Authorization: Bearer <JWT_TOKEN>
apikey: <SUPABASE_ANON_KEY>
Content-Type: application/json
Getting a Token
const { data: { session } } = await supabase.auth.getSession();
const token = session?.access_token;
Profiles
List Profiles
GET /profiles
Query parameters:
| Parameter | Description | Example |
|---|---|---|
| select | Columns to return | *,teams(name) |
| status | Filter by status | eq.active |
| team_id | Filter by team | eq.<uuid> |
| order | Sort order | full_name.asc |
Example:
GET /profiles?select=id,full_name,email,status&status=eq.active&order=full_name.asc
Get Single Profile
GET /profiles?id=eq.<uuid>
Update Profile
PATCH /profiles?id=eq.<uuid>
Content-Type: application/json
{
"full_name": "John Doe",
"team_id": "<team-uuid>"
}
Leave Requests
List Leave Requests
GET /leave_requests
Query parameters:
| Parameter | Description | Example |
|---|---|---|
| user_id | Filter by employee | eq.<uuid> |
| status | Filter by status | eq.pending |
| start_date | Filter by start | gte.2025-01-01 |
| end_date | Filter by end | lte.2025-12-31 |
Example with joins:
GET /leave_requests?select=*,profiles(full_name),leave_types(name_en)&status=eq.pending
Create Leave Request
POST /leave_requests
Content-Type: application/json
{
"user_id": "<user-uuid>",
"leave_type_id": "<type-uuid>",
"start_date": "2025-02-01",
"end_date": "2025-02-05",
"working_days": 5,
"reason": "Family vacation"
}
Update Leave Request (Approve/Reject)
PATCH /leave_requests?id=eq.<uuid>
Content-Type: application/json
{
"status": "approved",
"approver_user_id": "<approver-uuid>",
"approver_comment": "Approved. Enjoy your vacation!",
"approved_at": "2025-01-15T10:00:00Z"
}
Teams
List Teams
GET /teams?select=*,profiles!teams_lead_user_id_fkey(full_name)
Create Team
POST /teams
Content-Type: application/json
{
"name": "Engineering",
"lead_user_id": "<leader-uuid>"
}
Get Team Members
GET /profiles?team_id=eq.<team-uuid>&select=id,full_name,email
Translations
Get All Translations
GET /translations?select=key,croatian,english,russian,hindi
Get by Category
GET /translations?category=eq.common
Upsert Translation
POST /translations
Content-Type: application/json
Prefer: resolution=merge-duplicates
{
"key": "common.save",
"croatian": "Spremi",
"english": "Save",
"russian": "Сохранить",
"hindi": "सहेजें",
"category": "common"
}
Holiday Schemes
List Schemes
GET /holiday_schemes?is_active=eq.true&order=year.desc
Get Scheme with Holidays
GET /holiday_schemes?id=eq.<uuid>&select=*,holidays(*)
RPC Functions
Call database functions via RPC endpoint.
Get User Role
POST /rpc/get_user_role
Content-Type: application/json
{
"_user_id": "<user-uuid>"
}
Response:
"admin"
Check If Team Leader
POST /rpc/is_team_leader
Content-Type: application/json
{
"_user_id": "<user-uuid>"
}
Response:
true
Get Team Leadership
POST /rpc/get_team_leadership
Content-Type: application/json
{
"_user_id": "<user-uuid>"
}
Response:
[
{
"team_id": "uuid-here",
"team_name": "Engineering"
}
]
Filtering Reference
PostgREST filtering operators:
| Operator | Description | Example |
|---|---|---|
| eq | Equals | status=eq.active |
| neq | Not equals | status=neq.cancelled |
| gt | Greater than | working_days=gt.5 |
| gte | Greater or equal | start_date=gte.2025-01-01 |
| lt | Less than | working_days=lt.10 |
| lte | Less or equal | end_date=lte.2025-12-31 |
| like | Pattern match | full_name=like.*Smith* |
| ilike | Case-insensitive like | email=ilike.*@company.com |
| in | In list | status=in.(pending,approved) |
| is | Is null/true/false | end_date=is.null |
Pagination
Use Range header for pagination:
GET /profiles
Range: 0-9
Returns items 0-9 (first 10).
Response includes:
Content-Range: 0-9/100
Error Responses
401 Unauthorized
{
"message": "JWT expired",
"code": "PGRST301"
}
403 Forbidden (RLS)
{
"message": "new row violates row-level security policy",
"code": "42501"
}
404 Not Found
{
"message": "The result contains 0 rows",
"code": "PGRST116"
}
TypeScript Client
Using the Supabase client is recommended over raw REST calls:
import { supabase } from '@/integrations/supabase/client';
// List profiles
const { data, error } = await supabase
.from('profiles')
.select('*, teams(name)')
.eq('status', 'active')
.order('full_name');
// Create leave request
const { data, error } = await supabase
.from('leave_requests')
.insert({
user_id: userId,
leave_type_id: typeId,
start_date: '2025-02-01',
end_date: '2025-02-05',
working_days: 5
})
.select()
.single();
// Call RPC function
const { data: role } = await supabase
.rpc('get_user_role', { _user_id: userId });