Skip to main content

@tuturuuu/supabase

The @tuturuuu/supabase package provides type-safe Supabase client utilities for the Tuturuuu platform with different privilege levels and execution contexts.

Installation

Client Types

createClient() - User-Scoped Client

Use Case: Standard operations respecting Row-Level Security (RLS) policies. Context: Server Components, Server Actions, API Routes Privilege Level: Current authenticated user
Important:
  • ✅ Respects RLS policies
  • ✅ Safe for user-initiated operations
  • ❌ Cannot bypass workspace isolation
  • ❌ Cannot perform privileged operations

createAdminClient() - Service Role Client

Use Case: Privileged operations bypassing RLS (admin tasks, system operations). Context: Server-side only (never expose to client) Privilege Level: Service role (bypasses RLS)
⚠️ Critical Security Warning:
  • ✅ Only use server-side
  • ✅ Only for privileged operations
  • ❌ NEVER expose to client
  • ❌ NEVER use for user-initiated queries (always prefer createClient())
Return type note: createAdminClient() has the signature ({ noCookie }?: { noCookie?: boolean }) => SupabaseClient | Promise<SupabaseClient>. The default call (createAdminClient() with noCookie: false) resolves cookies via next/headers, so it returns a Promiseawait it as shown above. The createAdminClient({ noCookie: true }) variant returns a client synchronously. Awaiting either form is safe (await on a non-Promise is a no-op), so prefer await createAdminClient() everywhere for consistency.

createDynamicClient() - Middleware Client

Use Case: Middleware and edge runtime contexts. Context: Next.js middleware, edge functions

Common Patterns

Authentication

Revalidated Session User

Use resolveAuthenticatedSessionUser() from @tuturuuu/supabase/next/auth-session-user when shared server code needs the current Supabase session user. The helper may probe auth.getClaims() for compatibility or timing context, but it must revalidate the session with auth.getUser() before returning a user. Treat JWT claims as verified token contents, not current account state. Do not authorize privileged routes, staff-only checks, or createAdminClient() flows from claim-derived email, role, or metadata alone. For those paths, make the authorization decision from the Auth-server user returned by getUser() so revoked, deleted, or offboarded sessions are rejected before any service-role operation runs.

Get Current User

Server Action with Auth Check

Workspace Operations

Get User’s Workspaces

Check Workspace Permission

Data Fetching

Paginated Query

Filtered Query with Relations

Realtime Subscriptions

The browser createClient() from @tuturuuu/supabase/next/client is synchronous (returns a SupabaseClient, not a Promise), so it is used without await. Note this client is deprecated for general CRUD/storage — route those through @tuturuuu/internal-api — but it remains valid for realtime channel subscriptions, which are not covered by the internal API client.

Type Safety

Using Generated Types

Custom Type Helpers

Error Handling

Standard Pattern

With Try-Catch

Storage Operations

Upload File

Download File

Best Practices

✅ DO

  1. Use createClient() by default
  2. Check authentication early
  3. Handle errors explicitly
  4. Use TypeScript types
  5. Limit data fetching

❌ DON’T

  1. Never use createAdminClient() for user operations
  2. If you must use createAdminClient(), enforce membership and RBAC before the admin-backed operation
  3. Keep workspace-scoped admin mutations filtered by workspace ID
  4. Don’t expose service role key
  5. Don’t ignore errors
  6. Don’t fetch unnecessary data

Testing

Mock Supabase Client

Test Example

External Resources