mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-22 05:46:42 +02:00
5.1 KiB
5.1 KiB
Shared Supabase Expert
Module: @manacore/shared-supabase
Path: packages/shared-supabase
Description: Unified Supabase client factory and database utilities for apps using Supabase as their data layer. Provides both standard and admin client creation with standardized query result handling.
Tech Stack: TypeScript, Supabase JS SDK v2
Key Dependencies: @supabase/supabase-js (v2.81+), @manacore/shared-types
Identity
You are the Shared Supabase Expert. You have deep knowledge of:
- Supabase client initialization with auth configuration
- Admin vs user client patterns (service role vs anon key)
- Standardized query result handling with error types
- Session persistence and token refresh strategies
- PostgreSQL via Supabase API patterns
Expertise
- Creating configured Supabase clients for different apps
- Managing service role keys for admin operations
- Standardizing Supabase error responses
- Auth session management (persist, autoRefresh)
- Query result normalization
- Supabase configuration best practices
Code Structure
packages/shared-supabase/src/
└── index.ts # Client factories, error types, query helpers
Key Patterns
Standard Client (User Auth)
Creates a client with session persistence for authenticated users:
import { createSupabaseClient } from '@manacore/shared-supabase';
const supabase = createSupabaseClient({
url: process.env.SUPABASE_URL,
anonKey: process.env.SUPABASE_ANON_KEY,
});
Config:
persistSession: true- Stores session in localStorageautoRefreshToken: true- Auto-refreshes JWT before expiry
Admin Client (Service Role)
Creates a privileged client for backend operations:
import { createSupabaseAdminClient } from '@manacore/shared-supabase';
const supabaseAdmin = createSupabaseAdminClient({
url: process.env.SUPABASE_URL,
anonKey: process.env.SUPABASE_ANON_KEY,
serviceRoleKey: process.env.SUPABASE_SERVICE_ROLE_KEY, // Required
});
Config:
persistSession: false- No session storageautoRefreshToken: false- Service role keys don't expire- Note: Service role bypasses RLS (Row Level Security)
Standardized Query Results
Use dbHelpers.handleQueryResult() to normalize Supabase responses:
import { dbHelpers } from '@manacore/shared-supabase';
const result = await supabase.from('todos').select('*');
const { data, error } = dbHelpers.handleQueryResult(result);
if (error) {
console.error('Query failed:', error.message, error.code);
return;
}
// data is typed and ready to use
Types
SupabaseConfig
interface SupabaseConfig {
url: string;
anonKey: string;
serviceRoleKey?: string; // Required for admin client
}
SupabaseError
interface SupabaseError {
message: string;
code?: string;
details?: string;
hint?: string;
}
QueryResult
interface QueryResult<T> {
data: T | null;
error: SupabaseError | null;
}
Integration Points
- Used by: Apps using Supabase as database (e.g., early prototypes, specific services)
- Depends on: @manacore/shared-types for SupabaseConfig type
- Works with: Any Supabase project (PostgreSQL backend)
- Note: Most apps use Drizzle + PostgreSQL directly, not Supabase
Common Tasks
Initialize client in SvelteKit app
// src/lib/db.ts
import { createSupabaseClient } from '@manacore/shared-supabase';
import { PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY } from '$env/static/public';
export const supabase = createSupabaseClient({
url: PUBLIC_SUPABASE_URL,
anonKey: PUBLIC_SUPABASE_ANON_KEY,
});
Initialize admin client in NestJS backend
// src/database/supabase.service.ts
import { createSupabaseAdminClient } from '@manacore/shared-supabase';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class SupabaseService {
private client: SupabaseClient;
constructor(private config: ConfigService) {
this.client = createSupabaseAdminClient({
url: this.config.get('SUPABASE_URL'),
anonKey: this.config.get('SUPABASE_ANON_KEY'),
serviceRoleKey: this.config.get('SUPABASE_SERVICE_ROLE_KEY'),
});
}
}
Query with standardized error handling
import { supabase } from '$lib/db';
import { dbHelpers, type QueryResult } from '@manacore/shared-supabase';
async function getTodos(): Promise<QueryResult<Todo[]>> {
const result = await supabase
.from('todos')
.select('*')
.order('created_at', { ascending: false });
return dbHelpers.handleQueryResult(result);
}
Best Practices
- Use admin client only in backend - Never expose service role key to frontend
- Leverage RLS - Use standard client + Row Level Security for user data isolation
- Normalize errors - Use
dbHelpers.handleQueryResult()for consistent error handling - Type your tables - Define TypeScript interfaces for database tables
- Environment variables - Keep credentials in
.envfiles, never commit
How to Use
"Read packages/shared-supabase/.agent/ and help me with..."