From e81c714aaef4025724435e2cac9ac759a4e987fd Mon Sep 17 00:00:00 2001 From: Alaister Young Date: Mon, 22 Jun 2026 21:37:15 +0800 Subject: [PATCH] refactor(studio): lazy self-hosted admin client + enforce in API routes (from #46424) (#47104) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 ✓. ## 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. --------- Co-authored-by: Alaister Young <10985857+alaister@users.noreply.github.com> Co-authored-by: Ali Waseem --- apps/studio/eslint.config.cjs | 55 +++++++++++++++---- apps/studio/lib/api/self-hosted-admin.ts | 22 ++++++++ .../platform/auth/[ref]/users/[id]/factors.ts | 4 +- .../platform/auth/[ref]/users/[id]/index.ts | 4 +- .../api/platform/auth/[ref]/users/index.ts | 4 +- .../storage/[ref]/buckets/[id]/empty.ts | 4 +- .../storage/[ref]/buckets/[id]/index.ts | 4 +- .../[ref]/buckets/[id]/objects/download.ts | 4 +- .../[ref]/buckets/[id]/objects/index.ts | 4 +- .../[ref]/buckets/[id]/objects/list.ts | 4 +- .../[ref]/buckets/[id]/objects/move.ts | 4 +- .../[ref]/buckets/[id]/objects/public-url.ts | 4 +- .../[ref]/buckets/[id]/objects/sign-multi.ts | 4 +- .../[ref]/buckets/[id]/objects/sign.ts | 4 +- .../platform/storage/[ref]/buckets/index.ts | 4 +- .../[ref]/vector-buckets/[id]/index.ts | 4 +- .../[id]/indexes/[indexName].ts | 4 +- .../vector-buckets/[id]/indexes/index.ts | 4 +- .../storage/[ref]/vector-buckets/index.ts | 4 +- 19 files changed, 82 insertions(+), 63 deletions(-) create mode 100644 apps/studio/lib/api/self-hosted-admin.ts diff --git a/apps/studio/eslint.config.cjs b/apps/studio/eslint.config.cjs index 693497c89ff..83e90b2ae12 100644 --- a/apps/studio/eslint.config.cjs +++ b/apps/studio/eslint.config.cjs @@ -3,6 +3,37 @@ const barrelFiles = require('eslint-plugin-barrel-files') const jsxA11y = require('eslint-plugin-jsx-a11y') const supabaseConfig = require('eslint-config-supabase/next') +// Analytics SQL wire boundary — see the block below for context. Shared so the +// API/route block can re-include it (flat config replaces, not merges, a rule's +// options when blocks overlap, so the later block must carry these forward). +const ANALYTICS_SQL_RESTRICTED_SYNTAX = [ + { + selector: + "CallExpression[callee.name=/^(post|get)$/][arguments.0.value='/platform/projects/{ref}/analytics/endpoints/logs.all']", + message: + 'Do not call the analytics logs.all endpoint directly. Route through executeAnalyticsSql in @/data/logs/execute-analytics-sql so the SafeLogSqlFragment brand is enforced at compile time.', + }, + { + selector: + "CallExpression[callee.name=/^(post|get)$/][arguments.0.value='/platform/projects/{ref}/analytics/endpoints/logs.all.otel']", + message: + 'Do not call the analytics logs.all.otel endpoint directly. Route through executeAnalyticsSql in @/data/logs/execute-analytics-sql so the SafeLogSqlFragment brand is enforced at compile time.', + }, +] + +// Ban constructing a Supabase client at module scope in API route files. The +// TanStack server imports the entire route tree at boot (loadEntries), so a +// module-scope createClient with an env var that's unset in that environment +// (e.g. SUPABASE_URL on platform) throws on import and 500s every route — a +// runtime-only failure that's painful to catch. Construct it lazily inside the +// handler instead (see lib/api/self-hosted-admin.ts). +const NO_MODULE_SCOPE_CREATE_CLIENT = { + selector: + ":matches(Program, ExportNamedDeclaration) > VariableDeclaration > VariableDeclarator > CallExpression[callee.name='createClient']", + message: + 'Do not construct a Supabase client at module scope in API route files — the TanStack server evaluates every route module at boot, so a missing env var (e.g. SUPABASE_URL on platform) crashes every route. Construct it lazily inside the handler (see lib/api/self-hosted-admin.ts).', +} + module.exports = defineConfig([ { files: ['**/*.ts', '**/*.tsx'] }, supabaseConfig, @@ -40,21 +71,21 @@ module.exports = defineConfig([ { files: ['**/*.ts', '**/*.tsx'], ignores: ['data/logs/execute-analytics-sql.ts'], + rules: { + 'no-restricted-syntax': ['error', ...ANALYTICS_SQL_RESTRICTED_SYNTAX], + }, + }, + // API route modules are eagerly imported by the TanStack server at boot, so + // module-scope side effects there are especially dangerous. This block also + // re-includes the analytics selectors because flat config replaces (not + // merges) a rule's options for overlapping files. + { + files: ['pages/api/**/*.ts', 'pages/api/**/*.tsx', 'routes/**/*.ts', 'routes/**/*.tsx'], rules: { 'no-restricted-syntax': [ 'error', - { - selector: - "CallExpression[callee.name=/^(post|get)$/][arguments.0.value='/platform/projects/{ref}/analytics/endpoints/logs.all']", - message: - 'Do not call the analytics logs.all endpoint directly. Route through executeAnalyticsSql in @/data/logs/execute-analytics-sql so the SafeLogSqlFragment brand is enforced at compile time.', - }, - { - selector: - "CallExpression[callee.name=/^(post|get)$/][arguments.0.value='/platform/projects/{ref}/analytics/endpoints/logs.all.otel']", - message: - 'Do not call the analytics logs.all.otel endpoint directly. Route through executeAnalyticsSql in @/data/logs/execute-analytics-sql so the SafeLogSqlFragment brand is enforced at compile time.', - }, + ...ANALYTICS_SQL_RESTRICTED_SYNTAX, + NO_MODULE_SCOPE_CREATE_CLIENT, ], }, }, diff --git a/apps/studio/lib/api/self-hosted-admin.ts b/apps/studio/lib/api/self-hosted-admin.ts new file mode 100644 index 00000000000..30b23f13508 --- /dev/null +++ b/apps/studio/lib/api/self-hosted-admin.ts @@ -0,0 +1,22 @@ +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) + }, +}) diff --git a/apps/studio/pages/api/platform/auth/[ref]/users/[id]/factors.ts b/apps/studio/pages/api/platform/auth/[ref]/users/[id]/factors.ts index de399d7143c..dc03b15282b 100644 --- a/apps/studio/pages/api/platform/auth/[ref]/users/[id]/factors.ts +++ b/apps/studio/pages/api/platform/auth/[ref]/users/[id]/factors.ts @@ -1,9 +1,7 @@ -import { createClient } from '@supabase/supabase-js' import { NextApiRequest, NextApiResponse } from 'next' import apiWrapper from '@/lib/api/apiWrapper' - -const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!) +import { selfHostedSupabaseAdmin as supabase } from '@/lib/api/self-hosted-admin' export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler) diff --git a/apps/studio/pages/api/platform/auth/[ref]/users/[id]/index.ts b/apps/studio/pages/api/platform/auth/[ref]/users/[id]/index.ts index fd1b9ab1b03..78d0186402b 100644 --- a/apps/studio/pages/api/platform/auth/[ref]/users/[id]/index.ts +++ b/apps/studio/pages/api/platform/auth/[ref]/users/[id]/index.ts @@ -1,9 +1,7 @@ -import { createClient } from '@supabase/supabase-js' import { NextApiRequest, NextApiResponse } from 'next' import apiWrapper from '@/lib/api/apiWrapper' - -const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!) +import { selfHostedSupabaseAdmin as supabase } from '@/lib/api/self-hosted-admin' export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler) diff --git a/apps/studio/pages/api/platform/auth/[ref]/users/index.ts b/apps/studio/pages/api/platform/auth/[ref]/users/index.ts index a048ff84e24..4e7be6cf014 100644 --- a/apps/studio/pages/api/platform/auth/[ref]/users/index.ts +++ b/apps/studio/pages/api/platform/auth/[ref]/users/index.ts @@ -1,9 +1,7 @@ -import { createClient } from '@supabase/supabase-js' import { NextApiRequest, NextApiResponse } from 'next' import apiWrapper from '@/lib/api/apiWrapper' - -const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!) +import { selfHostedSupabaseAdmin as supabase } from '@/lib/api/self-hosted-admin' export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler) diff --git a/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/empty.ts b/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/empty.ts index e452568d997..1d6987f90ce 100644 --- a/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/empty.ts +++ b/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/empty.ts @@ -1,9 +1,7 @@ -import { createClient } from '@supabase/supabase-js' import { NextApiRequest, NextApiResponse } from 'next' import apiWrapper from '@/lib/api/apiWrapper' - -const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!) +import { selfHostedSupabaseAdmin as supabase } from '@/lib/api/self-hosted-admin' export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler) diff --git a/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/index.ts b/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/index.ts index 06100984874..f986a0570d2 100644 --- a/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/index.ts +++ b/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/index.ts @@ -1,9 +1,7 @@ -import { createClient } from '@supabase/supabase-js' import { NextApiRequest, NextApiResponse } from 'next' import apiWrapper from '@/lib/api/apiWrapper' - -const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!) +import { selfHostedSupabaseAdmin as supabase } from '@/lib/api/self-hosted-admin' export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler) diff --git a/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/download.ts b/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/download.ts index 1544687520c..6edfca45816 100644 --- a/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/download.ts +++ b/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/download.ts @@ -1,9 +1,7 @@ -import { createClient } from '@supabase/supabase-js' import { NextApiRequest, NextApiResponse } from 'next' import apiWrapper from '@/lib/api/apiWrapper' - -const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!) +import { selfHostedSupabaseAdmin as supabase } from '@/lib/api/self-hosted-admin' export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler) diff --git a/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/index.ts b/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/index.ts index c8ff7567799..861d0e2f8a0 100644 --- a/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/index.ts +++ b/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/index.ts @@ -1,9 +1,7 @@ -import { createClient } from '@supabase/supabase-js' import { NextApiRequest, NextApiResponse } from 'next' import apiWrapper from '@/lib/api/apiWrapper' - -const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!) +import { selfHostedSupabaseAdmin as supabase } from '@/lib/api/self-hosted-admin' export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler) diff --git a/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/list.ts b/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/list.ts index 845cc22deab..d9e76d9fa2b 100644 --- a/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/list.ts +++ b/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/list.ts @@ -1,9 +1,7 @@ -import { createClient } from '@supabase/supabase-js' import { NextApiRequest, NextApiResponse } from 'next' import apiWrapper from '@/lib/api/apiWrapper' - -const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!) +import { selfHostedSupabaseAdmin as supabase } from '@/lib/api/self-hosted-admin' export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler) diff --git a/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/move.ts b/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/move.ts index 8598f42a62d..9978c8b17cf 100644 --- a/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/move.ts +++ b/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/move.ts @@ -1,9 +1,7 @@ -import { createClient } from '@supabase/supabase-js' import { NextApiRequest, NextApiResponse } from 'next' import apiWrapper from '@/lib/api/apiWrapper' - -const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!) +import { selfHostedSupabaseAdmin as supabase } from '@/lib/api/self-hosted-admin' export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler) diff --git a/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/public-url.ts b/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/public-url.ts index c0b01c6a871..886d19886d7 100644 --- a/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/public-url.ts +++ b/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/public-url.ts @@ -1,9 +1,7 @@ -import { createClient } from '@supabase/supabase-js' import { NextApiRequest, NextApiResponse } from 'next' import apiWrapper from '@/lib/api/apiWrapper' - -const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!) +import { selfHostedSupabaseAdmin as supabase } from '@/lib/api/self-hosted-admin' export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler) diff --git a/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/sign-multi.ts b/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/sign-multi.ts index c670f177322..472f20c2e5e 100644 --- a/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/sign-multi.ts +++ b/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/sign-multi.ts @@ -1,9 +1,7 @@ -import { createClient } from '@supabase/supabase-js' import { NextApiRequest, NextApiResponse } from 'next' import apiWrapper from '@/lib/api/apiWrapper' - -const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!) +import { selfHostedSupabaseAdmin as supabase } from '@/lib/api/self-hosted-admin' const wrappedHandler = (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler) diff --git a/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/sign.ts b/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/sign.ts index 26bf7c88d6e..f077d8201c8 100644 --- a/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/sign.ts +++ b/apps/studio/pages/api/platform/storage/[ref]/buckets/[id]/objects/sign.ts @@ -1,9 +1,7 @@ -import { createClient } from '@supabase/supabase-js' import { NextApiRequest, NextApiResponse } from 'next' import apiWrapper from '@/lib/api/apiWrapper' - -const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!) +import { selfHostedSupabaseAdmin as supabase } from '@/lib/api/self-hosted-admin' export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler) diff --git a/apps/studio/pages/api/platform/storage/[ref]/buckets/index.ts b/apps/studio/pages/api/platform/storage/[ref]/buckets/index.ts index 010e7b723e5..45d11d1895e 100644 --- a/apps/studio/pages/api/platform/storage/[ref]/buckets/index.ts +++ b/apps/studio/pages/api/platform/storage/[ref]/buckets/index.ts @@ -1,9 +1,7 @@ -import { createClient } from '@supabase/supabase-js' import { NextApiRequest, NextApiResponse } from 'next' import apiWrapper from '@/lib/api/apiWrapper' - -const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!) +import { selfHostedSupabaseAdmin as supabase } from '@/lib/api/self-hosted-admin' export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler) diff --git a/apps/studio/pages/api/platform/storage/[ref]/vector-buckets/[id]/index.ts b/apps/studio/pages/api/platform/storage/[ref]/vector-buckets/[id]/index.ts index da26596bbe3..b9b0f56cb84 100644 --- a/apps/studio/pages/api/platform/storage/[ref]/vector-buckets/[id]/index.ts +++ b/apps/studio/pages/api/platform/storage/[ref]/vector-buckets/[id]/index.ts @@ -1,9 +1,7 @@ -import { createClient } from '@supabase/supabase-js' import { NextApiRequest, NextApiResponse } from 'next' import apiWrapper from '@/lib/api/apiWrapper' - -const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!) +import { selfHostedSupabaseAdmin as supabase } from '@/lib/api/self-hosted-admin' // eslint-disable-next-line import/no-anonymous-default-export export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler) diff --git a/apps/studio/pages/api/platform/storage/[ref]/vector-buckets/[id]/indexes/[indexName].ts b/apps/studio/pages/api/platform/storage/[ref]/vector-buckets/[id]/indexes/[indexName].ts index 671361ae94f..a34c6f86c9d 100644 --- a/apps/studio/pages/api/platform/storage/[ref]/vector-buckets/[id]/indexes/[indexName].ts +++ b/apps/studio/pages/api/platform/storage/[ref]/vector-buckets/[id]/indexes/[indexName].ts @@ -1,9 +1,7 @@ -import { createClient } from '@supabase/supabase-js' import { NextApiRequest, NextApiResponse } from 'next' import apiWrapper from '@/lib/api/apiWrapper' - -const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!) +import { selfHostedSupabaseAdmin as supabase } from '@/lib/api/self-hosted-admin' // eslint-disable-next-line import/no-anonymous-default-export export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler) diff --git a/apps/studio/pages/api/platform/storage/[ref]/vector-buckets/[id]/indexes/index.ts b/apps/studio/pages/api/platform/storage/[ref]/vector-buckets/[id]/indexes/index.ts index 272fdc74a9d..cc3d5e071da 100644 --- a/apps/studio/pages/api/platform/storage/[ref]/vector-buckets/[id]/indexes/index.ts +++ b/apps/studio/pages/api/platform/storage/[ref]/vector-buckets/[id]/indexes/index.ts @@ -1,9 +1,7 @@ -import { createClient } from '@supabase/supabase-js' import { NextApiRequest, NextApiResponse } from 'next' import apiWrapper from '@/lib/api/apiWrapper' - -const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!) +import { selfHostedSupabaseAdmin as supabase } from '@/lib/api/self-hosted-admin' // eslint-disable-next-line import/no-anonymous-default-export export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler) diff --git a/apps/studio/pages/api/platform/storage/[ref]/vector-buckets/index.ts b/apps/studio/pages/api/platform/storage/[ref]/vector-buckets/index.ts index 2d1b91ee053..9aea341ef1d 100644 --- a/apps/studio/pages/api/platform/storage/[ref]/vector-buckets/index.ts +++ b/apps/studio/pages/api/platform/storage/[ref]/vector-buckets/index.ts @@ -1,9 +1,7 @@ -import { createClient } from '@supabase/supabase-js' import { NextApiRequest, NextApiResponse } from 'next' import apiWrapper from '@/lib/api/apiWrapper' - -const supabase = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_KEY!) +import { selfHostedSupabaseAdmin as supabase } from '@/lib/api/self-hosted-admin' // eslint-disable-next-line import/no-anonymous-default-export export default (req: NextApiRequest, res: NextApiResponse) => apiWrapper(req, res, handler)