From f9371c8ba30b711da4bf42d10f81c7f3a9a6bce2 Mon Sep 17 00:00:00 2001 From: GabeDahl Date: Sun, 7 Jun 2026 17:49:28 -0500 Subject: [PATCH] Replace manual context casting with Hono Env type --- docs/typescript-generics.md | 12 ++++++--- src/adapters/hono/middleware.test.ts | 37 +++++++++++++++++++++++++++- src/adapters/hono/middleware.ts | 9 ++++++- 3 files changed, 53 insertions(+), 5 deletions(-) diff --git a/docs/typescript-generics.md b/docs/typescript-generics.md index c2ed36f..814cd13 100644 --- a/docs/typescript-generics.md +++ b/docs/typescript-generics.md @@ -76,7 +76,7 @@ const { data: users } = await supabaseAdmin.from('profiles').select() ## Using with the Hono adapter -The Hono context variable is typed as `SupabaseContext` (without the generic). To get typed clients, assert the type when destructuring: +Pass an app environment type to `Hono()` so Hono knows the `supabaseContext` variable includes your generated database types: ```ts import { Hono } from 'hono' @@ -84,12 +84,18 @@ import { withSupabase } from '@supabase/server/adapters/hono' import type { SupabaseContext } from '@supabase/server' import type { Database } from './database.types.ts' -const app = new Hono() +type Env = { + Variables: { + supabaseContext: SupabaseContext + } +} + +const app = new Hono() app.use('*', withSupabase({ auth: 'user' })) app.get('/todos', async (c) => { - const { supabase } = c.var.supabaseContext as SupabaseContext + const { supabase } = c.var.supabaseContext const { data } = await supabase.from('todos').select('id, title') return c.json(data) }) diff --git a/src/adapters/hono/middleware.test.ts b/src/adapters/hono/middleware.test.ts index e37fcdc..46147fc 100644 --- a/src/adapters/hono/middleware.test.ts +++ b/src/adapters/hono/middleware.test.ts @@ -1,11 +1,34 @@ import { Hono } from 'hono' -import { describe, expect, it } from 'vitest' +import { describe, expect, expectTypeOf, it } from 'vitest' import type { SupabaseContext } from '../../types.js' import { withSupabase } from './middleware.js' type Env = { Variables: { supabaseContext: SupabaseContext } } +type Database = { + public: { + Tables: { + todos: { + Row: { id: number; title: string } + Insert: { id?: number; title: string } + Update: { id?: number; title?: string } + Relationships: [] + } + } + Views: {} + Functions: {} + Enums: {} + CompositeTypes: {} + } +} + +type TypedEnv = { + Variables: { + supabaseContext: SupabaseContext + } +} + describe('hono supabase middleware', () => { const env = { url: 'https://test.supabase.co', @@ -34,6 +57,18 @@ describe('hono supabase middleware', () => { expect(body.hasAdmin).toBe(true) }) + it('uses the Hono app env to type the Supabase context', async () => { + const app = new Hono() + app.use('*', withSupabase({ auth: 'none', env })) + app.get('/', (c) => { + const ctx = c.get('supabaseContext') + expectTypeOf(ctx).toEqualTypeOf>() + return c.json({ authMode: ctx.authMode }) + }) + const res = await app.request('/') + expect(res.status).toBe(200) + }) + it('throws HTTPException on auth failure', async () => { const app = new Hono() app.use('*', withSupabase({ auth: 'user', env })) diff --git a/src/adapters/hono/middleware.ts b/src/adapters/hono/middleware.ts index 33581d4..fe56bff 100644 --- a/src/adapters/hono/middleware.ts +++ b/src/adapters/hono/middleware.ts @@ -18,8 +18,15 @@ import type { SupabaseContext, WithSupabaseConfig } from '../../types.js' * ```ts * import { Hono } from 'hono' * import { withSupabase } from '@supabase/server/adapters/hono' + * import type { SupabaseContext } from '@supabase/server' * - * const app = new Hono() + * type Env = { + * Variables: { + * supabaseContext: SupabaseContext + * } + * } + * + * const app = new Hono() * app.use('*', withSupabase({ auth: 'user' })) * * app.get('/profile', async (c) => {