skills/supabase-client-patterns/SKILL.md
Which Supabase client to use in React (public vs token-auth vs withAuth), safe-query helpers (callSelect/callInsert/callUpdate), and SupabaseClientLike type. Use when querying Supabase from a React component, hook, or fetch function.
npx skillsauth add bkinsey808/songshare-effect supabase-client-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Requires: file-read, terminal (linting/testing). No network access needed.
For API-side Supabase usage see api/src/supabase/. This skill covers React-side clients only.
Use this skill when:
react/ to align with repo client and safe-query patterns.Execution workflow:
getSupabaseClientWithAuth by default).SupabaseClientLike in signatures and safe-query helpers for table operations.{ data, error } consistently and avoid direct raw client creation in React code.npm run lint.Output requirements:
| Function | Location | Auth | Use when |
| ----------------------------- | -------------------------------- | ---------------- | --------------------------------- |
| getPublicSupabaseClient | react/src/lib/supabase/client/ | Anon key only | Truly public data, no RLS needed |
| getSupabaseClient(token) | same | JWT in header | You already have the token string |
| getSupabaseClientWithAuth() | same | Auto-fetch token | Fetching data in a hook/component |
Default: always use getSupabaseClientWithAuth() unless you have a specific reason for the others.
import getSupabaseClientWithAuth from "@/react/lib/supabase/client/getSupabaseClientWithAuth";
const client = await getSupabaseClientWithAuth();
if (!client) {
// Handle unavailable client (env vars missing, token fetch failed)
return;
}
getSupabaseClientWithAuth automatically picks the right token (user JWT if signed in, visitor JWT if not) with 3-retry + exponential backoff.
The return type is SupabaseClientLike<Database> (from @/react/lib/supabase/client/SupabaseClientLike), not the raw SupabaseClient. This is an interface to keep code testable — use it in type signatures:
import type { SupabaseClientLike } from "@/react/lib/supabase/client/SupabaseClientLike";
import type { Database } from "@/shared/generated/supabaseTypes";
async function fetchSomething(client: SupabaseClientLike<Database>) { ... }
Prefer callSelect / callInsert / callUpdate from @/react/lib/supabase/client/safe-query/ over calling .from().select() directly. They provide type-safe table names and normalised PostgrestResponse<Row> returns.
import callSelect from "@/react/lib/supabase/client/safe-query/callSelect";
import callInsert from "@/react/lib/supabase/client/safe-query/callInsert";
import callUpdate from "@/react/lib/supabase/client/safe-query/callUpdate";
// Select
const response = await callSelect<SongRow, Database, "songs">(client, "songs", {
cols: "id, title, artist",
eq: { col: "community_id", val: communityId },
order: "title",
});
// Insert
const response = await callInsert(client, "songs", { id, title, artist });
// Update
const response = await callUpdate(
client,
"songs",
{ title },
{
eq: { col: "id", val: songId },
},
);
Supabase returns { data, error }. Always check both:
const { data, error } = await callSelect<SongRow, Database, "songs">(client, "songs");
if (error) {
// handle
return;
}
// data is SongRow[] | null
Use asPostgrestResponse from @/react/lib/test-utils/asPostgrestResponse to build typed mock responses. Mock callSelect (not the client itself):
import callSelect from "@/react/lib/supabase/client/safe-query/callSelect";
import asPostgrestResponse from "@/react/lib/test-utils/asPostgrestResponse";
vi.mock("@/react/lib/supabase/client/safe-query/callSelect");
const mockedCallSelect = vi.mocked(callSelect);
mockedCallSelect.mockResolvedValue(asPostgrestResponse({ data: [{ id: "s1" }] }));
For the client itself use getSupabaseClient.test-util from @/react/lib/supabase/client/.
createClient from @supabase/supabase-js directly in React codegetPublicSupabaseClient for any data with RLS policiesSupabaseClient<Database> in function signatures — use SupabaseClientLike<Database>@/shared/generated/supabaseTypesauthentication-system.realtime-rls-debugging.tools
Zustand state management patterns for this project — store creation, selectors, Immer middleware, async actions with loading states, devtools, persist, and testing. Use when authoring or editing Zustand stores (use*Store files) or components that subscribe to stores. Do NOT use for React component structure or TypeScript-only utilities.
testing
How to write, update, or split skill files in this repo. Use when creating a new SKILL.md, updating an existing one, or deciding whether to put content in a skill vs. docs/.
development
Complete guide for testing React hooks — renderHook, Documentation by Harness, installStore, fixtures, subscription patterns, lint/compiler traps, and pre-completion checklist. Read docs/testing/unit-test-hook-best-practices.md for the full reference.
development
Vitest unit test authoring for this repo — setup, mocking, API handler testing, and common pitfalls for non-hook code. Use when the user asks to add, update, fix, or review unit tests for utilities, components, API handlers, or scripts. Do NOT use for React hook tests — load unit-test-hook-best-practices instead.