From afe44ead104def0d18b2c100eccd5cf77e88905f Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Wed, 22 Jul 2026 16:17:44 +0300 Subject: [PATCH] fix: add Database type override to elysia adapter --- CHANGELOG.md | 3 +-- docs/typescript-generics.md | 19 ++++++++++++++++++ jsr.json | 11 ++-------- src/adapters/elysia/plugin.test.ts | 28 +++++++++++++++++++++++++- src/adapters/elysia/plugin.ts | 32 ++++++++++++++++++++---------- 5 files changed, 70 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f622ea..449028c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,9 @@ ## [1.4.0](https://github.com/supabase/server/compare/server-v1.3.1...server-v1.4.0) (2026-07-13) - ### Features -* explicit cors config shape ('default' | 'disabled' | { headers }) ([#102](https://github.com/supabase/server/issues/102)) ([297925f](https://github.com/supabase/server/commit/297925fa1149a61d148306fdb6ba28e66bc0a737)) +- explicit cors config shape ('default' | 'disabled' | { headers }) ([#102](https://github.com/supabase/server/issues/102)) ([297925f](https://github.com/supabase/server/commit/297925fa1149a61d148306fdb6ba28e66bc0a737)) ## [1.3.1](https://github.com/supabase/server/compare/server-v1.3.0...server-v1.3.1) (2026-07-03) diff --git a/docs/typescript-generics.md b/docs/typescript-generics.md index ec93ecd..cc26956 100644 --- a/docs/typescript-generics.md +++ b/docs/typescript-generics.md @@ -121,6 +121,25 @@ const todosApp = new Hono() app.route('/todos', todosApp) ``` +The elysia adapter works the same way: thread `Database` through the `withSupabase()` generic and elysia infers the `supabaseContext` type, so `supabaseContext.supabase` is a fully typed `SupabaseClient` in every handler. + +```ts +import { Elysia } from 'elysia' +import { withSupabase } from '@supabase/server/adapters/elysia' +import type { Database } from './database.types.ts' + +const app = new Elysia() + .use(withSupabase({ auth: 'user' })) + .get('/todos', async ({ supabaseContext }) => { + const { data } = await supabaseContext.supabase + .from('todos') + .select('id, title') + return data + }) + +app.listen(3000) +``` + ## Custom schema If your tables are in a schema other than `public`, pass it via `supabaseOptions`: diff --git a/jsr.json b/jsr.json index 67666e0..62d178e 100644 --- a/jsr.json +++ b/jsr.json @@ -11,14 +11,7 @@ "./adapters/nestjs": "./src/adapters/nestjs/index.ts" }, "publish": { - "include": [ - "src/**/*.ts", - "README.md", - "LICENSE" - ], - "exclude": [ - "src/**/*.test.ts", - "src/**/*.spec.ts" - ] + "include": ["src/**/*.ts", "README.md", "LICENSE"], + "exclude": ["src/**/*.test.ts", "src/**/*.spec.ts"] } } diff --git a/src/adapters/elysia/plugin.test.ts b/src/adapters/elysia/plugin.test.ts index a264aa6..1e90ee5 100644 --- a/src/adapters/elysia/plugin.test.ts +++ b/src/adapters/elysia/plugin.test.ts @@ -1,8 +1,22 @@ import { Elysia } from 'elysia' -import { describe, expect, it } from 'vitest' +import { describe, expect, expectTypeOf, it } from 'vitest' +import type { SupabaseContext } from '../../types.js' import { withSupabase } from './plugin.js' +type Database = { + public: { + Tables: { + todos: { + Row: { id: number; title: string } + Insert: { id?: number; title: string } + Update: { id?: number; title?: string } + Relationships: [] + } + } + } +} + describe('elysia supabase plugin', () => { const env = { url: 'https://test.supabase.co', @@ -28,6 +42,18 @@ describe('elysia supabase plugin', () => { expect(body.hasAdmin).toBe(true) }) + it('threads the Database generic into the Supabase context', async () => { + const app = new Elysia() + .use(withSupabase({ auth: 'none', env })) + .get('/', ({ supabaseContext }) => { + expectTypeOf(supabaseContext).toEqualTypeOf>() + return { authMode: supabaseContext.authMode } + }) + + const res = await app.handle(new Request('http://localhost/')) + expect(res.status).toBe(200) + }) + it('throws error on auth failure', async () => { const app = new Elysia() .use(withSupabase({ auth: 'user', env })) diff --git a/src/adapters/elysia/plugin.ts b/src/adapters/elysia/plugin.ts index f159366..7f01f1e 100644 --- a/src/adapters/elysia/plugin.ts +++ b/src/adapters/elysia/plugin.ts @@ -70,7 +70,9 @@ export class SupabaseError extends Error { // `{}` literals — switching to `object` or `Record` would not satisfy // the corresponding generic constraints. /* eslint-disable @typescript-eslint/no-empty-object-type */ -export function withSupabase(config?: Omit): Elysia< +export function withSupabase( + config?: Omit, +): Elysia< '', { decorator: {}; store: {}; derive: {}; resolve: {} }, { typebox: {}; error: { readonly SupabaseError: SupabaseError } }, @@ -85,10 +87,12 @@ export function withSupabase(config?: Omit): Elysia< {}, { derive: {} - resolve: { supabaseContext: SupabaseContext } + resolve: { supabaseContext: SupabaseContext } schema: {} standaloneSchema: {} - response: ExtractErrorFromHandle<{ supabaseContext: SupabaseContext }> + response: ExtractErrorFromHandle<{ + supabaseContext: SupabaseContext + }> }, { derive: {} @@ -101,15 +105,21 @@ export function withSupabase(config?: Omit): Elysia< /* eslint-enable @typescript-eslint/no-empty-object-type */ return new Elysia() .error({ SupabaseError }) - .resolve(async (ctx): Promise<{ supabaseContext: SupabaseContext }> => { - const existing = (ctx as { supabaseContext?: SupabaseContext }) - .supabaseContext - if (existing) return { supabaseContext: existing } + .resolve( + async (ctx): Promise<{ supabaseContext: SupabaseContext }> => { + const existing = ( + ctx as { supabaseContext?: SupabaseContext } + ).supabaseContext + if (existing) return { supabaseContext: existing } - const { data, error } = await createSupabaseContext(ctx.request, config) - if (error) throw new SupabaseError(error) + const { data, error } = await createSupabaseContext( + ctx.request, + config, + ) + if (error) throw new SupabaseError(error) - return { supabaseContext: data } - }) + return { supabaseContext: data } + }, + ) .as('scoped') }