@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
- ✅ 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)
- ✅ 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()withnoCookie: false) resolves cookies vianext/headers, so it returns aPromise—awaitit as shown above. ThecreateAdminClient({ noCookie: true })variant returns a client synchronously. Awaiting either form is safe (awaiton a non-Promise is a no-op), so preferawait 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
UseresolveAuthenticatedSessionUser() 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 browsercreateClient() 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
-
Use
createClient()by default -
Check authentication early
-
Handle errors explicitly
-
Use TypeScript types
-
Limit data fetching
❌ DON’T
-
Never use
createAdminClient()for user operations -
If you must use
createAdminClient(), enforce membership and RBAC before the admin-backed operation -
Keep workspace-scoped admin mutations filtered by workspace ID
-
Don’t expose service role key
-
Don’t ignore errors
-
Don’t fetch unnecessary data