5. Success Response Format
{
"data": {
...
},
"message": "Project created successfully."
}
No endpoint is complete until auth is checked, input is validated, errors are standardized, it is tested across all roles and failure cases, and the frontend has documentation.
| Use This | When |
|---|---|
Direct table query: supabase.from(...) |
Simple CRUD on one table with RLS |
| RPC / Postgres Function | Multi-step logic, atomic operations, cross-table writes, or complex business logic |
| Edge Function | Webhooks, third-party integrations, email sending, payment processing, logic requiring secrets |
Every input must be validated at three layers:
| Layer | What It Does |
|---|---|
| Frontend | UX feedback — required, format hints |
| API / RPC | Business rule enforcement — length, format, uniqueness |
| Database | Data integrity — NOT NULL, CHECK constraints, FK constraints |
{
"error": "VALIDATION_ERROR",
"message": "Validation failed.",
"fields": {
"name": "Name is required.",
"email": "Enter a valid email address."
}
}
{
"data": {
...
},
"message": "Project created successfully."
}
{
"error": "ERROR_CODE",
"message": "Human-readable description of the error."
}
| Code | Meaning | HTTP Status |
|---|---|---|
AUTH_REQUIRED | Not logged in | 401 |
FORBIDDEN | Logged in but no permission | 403 |
NOT_FOUND | Resource does not exist | 404 |
DUPLICATE | Already exists — unique constraint | 409 |
VALIDATION_ERROR | Invalid or missing input | 400 |
BUSINESS_RULE_VIOLATION | Failed a business rule | 422 |
SERVER_ERROR | Unexpected internal error | 500 |
Only applies when building a Postgres RPC function.
CREATE OR REPLACE FUNCTION create_project(
p_workspace_id UUID,
p_name TEXT,
p_description TEXT DEFAULT NULL
)
RETURNS JSON
LANGUAGE plpgsql
SECURITY INVOKER
AS $$
DECLARE
v_project projects%ROWTYPE;
BEGIN
IF p_name IS NULL OR trim(p_name) = '' THEN
RAISE EXCEPTION 'VALIDATION_ERROR: name is required';
END IF;
IF length(trim(p_name)) > 100 THEN
RAISE EXCEPTION 'VALIDATION_ERROR: name must be 100 characters or fewer';
END IF;
IF EXISTS (
SELECT 1
FROM projects
WHERE workspace_id = p_workspace_id
AND name = trim(p_name)
) THEN
RAISE EXCEPTION 'DUPLICATE: a project with this name already exists';
END IF;
INSERT INTO projects (workspace_id, name, description, created_by)
VALUES (p_workspace_id, trim(p_name), p_description, auth.uid())
RETURNING * INTO v_project;
RETURN json_build_object('data', row_to_json(v_project));
END;
$$;
Every API endpoint needs documentation before it is handed off to the frontend team.
Apply what you learned by building a real multi-step RPC function with atomic operations, validation, and full API documentation.
Open Task 03: Build a Create Project API