Files
supabase/apps/studio/lib/api/self-hosted-admin.ts
Alaister Young e81c714aae refactor(studio): lazy self-hosted admin client + enforce in API routes (from #46424) (#47104)
Extracted from the TanStack Start migration (#46424) to shrink that PR.

The self-hosted storage/auth API routes each constructed a module-scope
admin client (`createClient(process.env.SUPABASE_URL!,
process.env.SUPABASE_SERVICE_KEY!)`). Those env vars only exist on
self-hosted, so eager module-scope construction is wasteful on platform
and fragile on any runtime that evaluates an API module before its route
is hit (constructing with `undefined` credentials throws on import).

**Changed:**
- Add `lib/api/self-hosted-admin.ts` — `selfHostedSupabaseAdmin`, a
`Proxy` that defers `createClient(...)` until first property access
(inside a handler, i.e. on self-hosted where the vars are set).
- Swap **all 17** storage/auth/vector-bucket handlers from module-scope
`createClient(...)` to `import { selfHostedSupabaseAdmin as supabase }`.
- **Enforce it:** add an eslint `no-restricted-syntax` rule banning
module-scope `createClient` in `pages/api/**` + `routes/**` (now that
every flagged handler is lazy). The same eslint config block also
carries an analytics-SQL boundary rule — 0 violations on master.

Behaviour is unchanged (the client is still built lazily inside the
handler). This is also the change that makes those routes safe under
TanStack's single-handler module evaluation.

## To test
- Self-hosted Studio: storage buckets/objects, vector buckets, and auth
users operations work as before.

## Verification
studio lint (0 errors, both rules active) ✓ · studio typecheck ✓.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Summary by CodeRabbit

* **Refactor**
* Standardized self-hosted Supabase admin client usage across platform
authentication and storage endpoints, removing per-route client setup.
* Improved reliability by lazily creating the admin client only when
first used.
* **Chores / Tooling**
* Updated ESLint rules to prevent module-scope Supabase client creation
in API routes and to enforce safe analytics SQL access patterns.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com>
Co-authored-by: Ali Waseem <waseema393@gmail.com>
2026-06-22 13:37:15 +00:00

23 lines
1.0 KiB
TypeScript

import { createClient, SupabaseClient } from '@supabase/supabase-js'
// Lazy admin client for self-hosted API routes under
// `pages/api/platform/{auth,storage}/**`. SUPABASE_URL and
// SUPABASE_SERVICE_KEY are only set on self-hosted deployments — the
// platform build doesn't need these env vars. But on the TanStack Start
// server, every API route's module gets evaluated when the single function
// handler loads, regardless of whether its URL is hit. Without a lazy
// wrapper, constructing the client at module scope with undefined
// credentials would crash every request on platform.
//
// Proxy defers client construction until a property is actually accessed,
// which only happens inside the handler (i.e. on self-hosted where the env
// vars are set).
let _client: SupabaseClient | undefined
export const selfHostedSupabaseAdmin = new Proxy({} as SupabaseClient, {
get(_target, prop) {
_client ??= createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!)
return Reflect.get(_client, prop)
},
})