-
Notifications
You must be signed in to change notification settings - Fork 2
SupabaseClient
Pair\Services\SupabaseClient is a dependency-free HTTP bridge for server-side Supabase workflows.
It is intentionally limited to integration seams that reduce Pair project setup time:
- Storage object upload, download, listing, delete, public URLs, and signed URLs
- Supabase Auth user lookup from a user JWT
- Supabase Auth admin user lookup with the service role key
- PostgREST
selectand RPC calls for bridge workflows - Realtime WebSocket URL construction for external clients
It is not a replacement for Pair's ORM, migrations, user model, or authorization model. Pair projects should keep domain persistence in Pair models unless a specific Supabase bridge is explicitly useful.
SUPABASE_URL=https://project-ref.supabase.co
SUPABASE_ANON_KEY=
SUPABASE_SERVICE_ROLE_KEY=
SUPABASE_TIMEOUT=20
SUPABASE_CONNECT_TIMEOUT=5SUPABASE_ANON_KEY is used for user-scoped requests and Realtime URLs. SUPABASE_SERVICE_ROLE_KEY is used only for trusted server-side calls, such as admin Auth lookup and server-owned Storage operations.
Never expose the service role key in browser code, JavaScript bundles, mobile apps, screenshots, logs, or client-rendered HTML.
Pair does not reserve a core adapter key for Supabase. A project or optional pair/supabase package should register it with a project-owned key:
use Pair\Services\SupabaseClient;
$app->setAdapter('supabase', new SupabaseClient());
$supabase = $app->adapter('supabase', SupabaseClient::class);This keeps Supabase optional and avoids coupling Pair's ORM or auth model to a third-party platform.
$supabase->storageUpload(
'avatars',
'users/42/avatar.png',
file_get_contents($path),
'image/png',
[
'cacheControl' => '3600',
'upsert' => true,
]
);
$url = $supabase->storagePublicUrl('avatars', 'users/42/avatar.png');
$signed = $supabase->storageSignedUrl('private-files', 'reports/q1.pdf', 300);Server-side Storage helpers default to the service role key because they are intended for trusted application code. For user-scoped flows, pass a user token:
$supabase->storageUpload('avatars', 'users/42/avatar.png', $bytes, 'image/png', [
'bearerToken' => $userAccessToken,
'serviceRole' => false,
]);Keep application-level ownership checks in Pair before calling the provider. Supabase RLS can be a second layer, not the only authorization boundary in Pair controllers.
$user = $supabase->authUser($userAccessToken);
$adminUser = $supabase->authAdminGetUser($supabaseUserId);Use authUser() when a Pair API receives a Supabase access token and needs an authoritative user lookup from Supabase Auth.
Use authAdminGetUser() only from trusted server-side code. Do not map Supabase users directly into Pair sessions without a project-owned linking policy.
$rows = $supabase->restSelect('profiles', [
'id' => 'eq.42',
], [
'select' => 'id,email,full_name',
'bearerToken' => $userAccessToken,
]);
$result = $supabase->rpc('sync_profile_projection', [
'user_id' => '42',
], [
'serviceRole' => true,
'prefer' => 'return=representation',
]);Use PostgREST and RPC bridges for integration boundaries, reporting projections, imports, exports, or Supabase-owned tables. Do not use them as a parallel ORM for Pair-owned domain tables.
$socketUrl = $supabase->realtimeWebSocketUrl([
'vsn' => '2.0.0',
]);The method only builds the WebSocket URL. Pair does not implement the Realtime protocol client in PHP; browser or worker code should use the official Supabase client when it needs channel joins, presence, broadcast, or Postgres change subscriptions.
- Keep service role usage server-only and explicit.
- Prefer user
bearerTokenoptions for user-scoped REST and Storage calls. - Keep Pair authorization checks before provider calls.
- Do not treat Supabase REST as a replacement for Pair ActiveRecord.
- Keep Realtime protocol handling outside Pair core unless a project has a concrete worker requirement.
See also: Integrations, Configuration file, AdapterRegistry, Application, ActiveRecord.