| name | frontend-supabase-sdk |
| description | Use when making direct Supabase SDK calls in TypeScript files (hooks, components, routes, utilities) |
Frontend Supabase SDK Usage
Naming Convention for SDK Responses
Pattern
const sb_Service[StringParam]_Operation = await supabase.service.method()
Components:
sb_ - Prefix for all Supabase SDK responses
Service - The Supabase service: Auth, From, Functions, Storage
[StringParam] - String parameters converted to PascalCase and concatenated directly (no underscore)
Operation - The primary operation: Select, Insert, Update, Delete, Invoke, etc.
String Parameter Conversion
snake_case → PascalCase
"organization_members" → OrganizationMembers
"rel_files_tags" → RelFilesTags
kebab-case → PascalCase
"generate-upload-url" → GenerateUploadUrl
"delete-file" → DeleteFile
Concatenation: String parameters are concatenated directly with the service/method name (no underscore between them)
Primary Operation Only
Use only the main operation, ignore modifiers like .single(), .eq(), .maybeSingle(), etc.
const sb_FromProjects_Select = await supabase.from("projects").select().eq("id", id).single();
const sb_FromProjects_SelectEqSingle = await supabase
.from("projects")
.select()
.eq("id", id)
.single();
Core Examples
Database Operations (from)
const sb_FromProjects_Insert = await supabase.from("projects").insert(body).select().single();
const sb_FromOrganizationMembers_Select = await supabase
.from("organization_members")
.select("*, organizations(*)")
.eq("user_id", userId);
const sb_FromProjects_Update = await supabase
.from("projects")
.update(body)
.eq("id", id)
.select()
.single();
const sb_FromRelFilesTags_Delete = await supabase
.from("rel_files_tags")
.delete()
.eq("file_id", fileId);
Auth Operations
const sb_Auth_GetUser = await supabase.auth.getUser();
const user = sb_Auth_GetUser.data.user;
const sb_Auth_GetSession = await supabase.auth.getSession();
const session = sb_Auth_GetSession.data.session;
const sb_Auth_SignOut = await supabase.auth.signOut();
Functions (Edge Functions)
const sb_Functions_InvokeGenerateUploadUrl = await supabase.functions.invoke(
"generate-upload-url",
{ body: { fileName, projectId } }
);
Storage Operations
const sb_StorageProjectFiles_Upload = await supabase.storage
.from("project-files")
.upload(path, file);
const sb_StorageProjectFiles_Download = await supabase.storage.from("project-files").download(path);
Accessing Data and Errors
const sb_FromProjects_Select = await supabase.from("projects").select();
if (sb_FromProjects_Select.error) {
console.error(sb_FromProjects_Select.error);
throw sb_FromProjects_Select.error;
}
const projects = sb_FromProjects_Select.data;
Why This Convention?
❌ Problems with Destructuring
Naming Conflicts:
const { data, error } = await supabase.from("projects").insert();
const { data, error } = await supabase.from("files").select();
Ugly Inline Renaming:
const { data: uploadData, error: urlError } =
await supabase.functions.invoke("generate-upload-url");
const {
data: { user },
error: userError,
} = await supabase.auth.getUser();
const { data: metadata, error: metadataError } = await supabase.from("files").insert();
Inconsistent Patterns:
const { data: projects, error: projectsError };
const { data: projectData, error: projectErr };
✅ Benefits of sb_* Pattern
- No Conflicts: Each variable has unique, descriptive name
- Explicit and Verbose: Immediately clear what each variable represents
- Consistent Convention: Always follows same pattern - no guessing
- Better for AI/LLM: Systematic and reliably followed
- Self-Documenting: Variable name describes the operation
Common Patterns
Error Handling
const sb_FromProjects_Insert = await supabase.from("projects").insert(body);
if (sb_FromProjects_Insert.error) {
console.error("Failed to create project:", sb_FromProjects_Insert.error);
throw sb_FromProjects_Insert.error;
}
return sb_FromProjects_Insert.data;
Early Return
const sb_Auth_GetUser = await supabase.auth.getUser();
if (sb_Auth_GetUser.error || !sb_Auth_GetUser.data.user) {
return null;
}
const user = sb_Auth_GetUser.data.user;
Nested Data
const sb_Auth_GetSession = await supabase.auth.getSession();
const session = sb_Auth_GetSession.data.session;
Usage in Different Contexts
In Query Hooks (useQ_*):
const queryFn = async (projectId: string) => {
const sb_FromFiles_Select = await supabase
.from("files")
.select("*, rel_files_tags(file_tags(*))")
.eq("project_id", projectId);
if (sb_FromFiles_Select.error) throw sb_FromFiles_Select.error;
return sb_FromFiles_Select.data;
};
In Mutation Hooks (useM_*):
const mutationFn = async (body: CreateProjectBody) => {
const sb_FromProjects_Insert = await supabase.from("projects").insert(body).select().single();
if (sb_FromProjects_Insert.error) throw sb_FromProjects_Insert.error;
return sb_FromProjects_Insert.data;
};
In Route Guards:
export const loader = async () => {
const sb_Auth_GetSession = await supabase.auth.getSession();
const session = sb_Auth_GetSession.data.session;
if (!session) {
throw redirect({ to: "/login" });
}
return { session };
};
Related Skills
- frontend-query-hooks - Query hooks using this convention
- frontend-mutation-hooks - Mutation hooks using this convention
- frontend-supabase-auth - Auth-specific patterns
For additional examples: .claude/skills/frontend-supabase-sdk/examples.md