From 8f19dfae42e7f4efd77bbdadb3ab6149ca71c35c Mon Sep 17 00:00:00 2001 From: Katerina Skroumpelou Date: Fri, 11 Sep 2026 18:38:07 +0300 Subject: [PATCH] fix: default Database generic to any, matching createClient() --- docs/adapters/nestjs.md | 2 +- docs/api-reference.md | 12 ++++++------ docs/typescript-generics.md | 2 ++ src/adapters/elysia/plugin.ts | 8 ++++++-- src/adapters/hono/middleware.ts | 8 ++++++-- src/core/create-admin-client.ts | 4 ++-- src/core/create-context-client.ts | 4 ++-- src/create-supabase-context.ts | 8 ++++++-- src/middleware/admin-client/index.ts | 4 ++-- src/middleware/client/index.ts | 7 +++++-- src/types.ts | 12 +++++++++++- src/with-supabase.ts | 5 +++-- 12 files changed, 52 insertions(+), 24 deletions(-) diff --git a/docs/adapters/nestjs.md b/docs/adapters/nestjs.md index 0229432..80e753a 100644 --- a/docs/adapters/nestjs.md +++ b/docs/adapters/nestjs.md @@ -40,7 +40,7 @@ export class GamesController { ### Typing your database -The guard does not thread a `Database` generic, so `@SupabaseCtx()` resolves to `SupabaseContext` by default. To get typed table access, annotate the parameter at the handler: +The guard does not thread a `Database` generic, so `@SupabaseCtx()` resolves to `SupabaseContext` by default. To get typed table access, annotate the parameter at the handler: ```ts import type { SupabaseContext } from '@supabase/server' diff --git a/docs/api-reference.md b/docs/api-reference.md index 7487139..aaa78be 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -9,7 +9,7 @@ Complete reference for every export, organized by entry point. ### withSupabase ```ts -function withSupabase( +function withSupabase( config: WithSupabaseConfig, handler: (req: Request, ctx: SupabaseContext) => Promise, ): (req: Request) => Promise @@ -25,7 +25,7 @@ Wraps a fetch handler with auth, CORS, and client creation. Returns a `(req: Req - Reading the raw `req.body` stream bypasses the buffer, so a handler that forwards the request with `fetch()` after another layer has read the body rebuilds it from `await req.arrayBuffer()`. ```ts -function withSupabase( +function withSupabase( config: WithSupabaseConfig, ): Entry> ``` @@ -56,7 +56,7 @@ Entries before `withSupabase` see every request, including unauthenticated ones, ### createSupabaseContext ```ts -function createSupabaseContext( +function createSupabaseContext( request: Request, options?: WithSupabaseConfig, ): Promise< @@ -122,7 +122,7 @@ Resolves Supabase environment configuration from runtime variables. `SUPABASE_UR ### createContextClient ```ts -function createContextClient( +function createContextClient( options?: CreateContextClientOptions, ): SupabaseClient ``` @@ -138,7 +138,7 @@ Configured with: ### createAdminClient ```ts -function createAdminClient( +function createAdminClient( options?: CreateAdminClientOptions, ): SupabaseClient ``` @@ -549,7 +549,7 @@ withSupabase({ auth: 'none' }, handler) // no credentials required ### SupabaseContext\ ```ts -interface SupabaseContext { +interface SupabaseContext { supabase: SupabaseClient supabaseAdmin: SupabaseClient userClaims: UserClaims | null diff --git a/docs/typescript-generics.md b/docs/typescript-generics.md index cc26956..8919791 100644 --- a/docs/typescript-generics.md +++ b/docs/typescript-generics.md @@ -4,6 +4,8 @@ All client-creating functions accept a `Database` generic parameter. When you pass your generated database types, every `.from('table').select()` call is fully typed — column names, return types, insert shapes, and RPC signatures. +Without one, `Database` defaults to `any` — the same default `createClient()` uses in `@supabase/supabase-js` — so `.from()`, `.select()`, `.insert()`, and `.update()` all type-check without a generated schema. + ## Generating types Use the Supabase CLI to generate TypeScript types from your database schema: diff --git a/src/adapters/elysia/plugin.ts b/src/adapters/elysia/plugin.ts index 7f01f1e..8d94ddd 100644 --- a/src/adapters/elysia/plugin.ts +++ b/src/adapters/elysia/plugin.ts @@ -2,7 +2,11 @@ import { Elysia, type ExtractErrorFromHandle } from 'elysia' import { createSupabaseContext } from '../../create-supabase-context.js' import type { AuthError } from '../../errors.js' -import type { SupabaseContext, WithSupabaseConfig } from '../../types.js' +import type { + SupabaseContext, + UntypedDatabase, + WithSupabaseConfig, +} from '../../types.js' /** * Wraps an {@link AuthError} as an Elysia-compatible error. @@ -70,7 +74,7 @@ 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( +export function withSupabase( config?: Omit, ): Elysia< '', diff --git a/src/adapters/hono/middleware.ts b/src/adapters/hono/middleware.ts index 1dd00a6..dce0e36 100644 --- a/src/adapters/hono/middleware.ts +++ b/src/adapters/hono/middleware.ts @@ -3,7 +3,11 @@ import { createMiddleware } from 'hono/factory' import { HTTPException } from 'hono/http-exception' import { createSupabaseContext } from '../../create-supabase-context.js' -import type { SupabaseContext, WithSupabaseConfig } from '../../types.js' +import type { + SupabaseContext, + UntypedDatabase, + WithSupabaseConfig, +} from '../../types.js' /** * Hono middleware that creates a {@link SupabaseContext} and stores it in `c.var.supabaseContext`. @@ -40,7 +44,7 @@ import type { SupabaseContext, WithSupabaseConfig } from '../../types.js' * * @category Adapters */ -export function withSupabase( +export function withSupabase( config?: Omit, ): MiddlewareHandler<{ Variables: { supabaseContext: SupabaseContext } diff --git a/src/core/create-admin-client.ts b/src/core/create-admin-client.ts index 6a0db3f..31a886d 100644 --- a/src/core/create-admin-client.ts +++ b/src/core/create-admin-client.ts @@ -5,7 +5,7 @@ import { MissingDefaultSecretKeyError, MissingSecretKeyError, } from '../errors.js' -import type { CreateAdminClientOptions } from '../types.js' +import type { CreateAdminClientOptions, UntypedDatabase } from '../types.js' import { resolveEnv } from './resolve-env.js' /** @@ -39,7 +39,7 @@ import { resolveEnv } from './resolve-env.js' * * @category Primitives */ -export function createAdminClient( +export function createAdminClient( options?: CreateAdminClientOptions, ): SupabaseClient { const { data: resolved, error } = resolveEnv(options?.env) diff --git a/src/core/create-context-client.ts b/src/core/create-context-client.ts index a338c8f..3803787 100644 --- a/src/core/create-context-client.ts +++ b/src/core/create-context-client.ts @@ -5,7 +5,7 @@ import { MissingDefaultPublishableKeyError, MissingPublishableKeyError, } from '../errors.js' -import type { CreateContextClientOptions } from '../types.js' +import type { CreateContextClientOptions, UntypedDatabase } from '../types.js' import { resolveEnv } from './resolve-env.js' /** @@ -37,7 +37,7 @@ import { resolveEnv } from './resolve-env.js' * * @category Primitives */ -export function createContextClient( +export function createContextClient( options?: CreateContextClientOptions, ): SupabaseClient { const { data: resolved, error } = resolveEnv(options?.env) diff --git a/src/create-supabase-context.ts b/src/create-supabase-context.ts index 14fb9ac..c3759f3 100644 --- a/src/create-supabase-context.ts +++ b/src/create-supabase-context.ts @@ -7,7 +7,11 @@ import { EnvError, Errors, } from './errors.js' -import type { SupabaseContext, WithSupabaseConfig } from './types.js' +import type { + SupabaseContext, + UntypedDatabase, + WithSupabaseConfig, +} from './types.js' /** * Creates a {@link SupabaseContext} directly from a request. @@ -32,7 +36,7 @@ import type { SupabaseContext, WithSupabaseConfig } from './types.js' * const { data } = await ctx.supabase.rpc('get_my_items') * ``` */ -export async function createSupabaseContext( +export async function createSupabaseContext( request: Request, options?: WithSupabaseConfig, ): Promise< diff --git a/src/middleware/admin-client/index.ts b/src/middleware/admin-client/index.ts index f092e6c..8aef214 100644 --- a/src/middleware/admin-client/index.ts +++ b/src/middleware/admin-client/index.ts @@ -6,7 +6,7 @@ import { createAdminClient } from '../../core/create-admin-client.js' import { lazyClient } from '../../core/lazy-client.js' import { readUpstreamAuth } from '../../core/read-upstream-auth.js' import { CreateSupabaseClientError, EnvError, Errors } from '../../errors.js' -import type { CreateAdminClientOptions } from '../../types.js' +import type { CreateAdminClientOptions, UntypedDatabase } from '../../types.js' /** * **Alpha.** Configuration for {@link withSupabaseAdminClient} — the same @@ -90,7 +90,7 @@ const base = defineMiddleware< * @alpha * @category Middleware */ -export function withSupabaseAdminClient( +export function withSupabaseAdminClient( config?: WithSupabaseAdminClientConfig, ): Entry<{ supabaseAdmin: SupabaseClient }> { return base(config) as unknown as Entry<{ diff --git a/src/middleware/client/index.ts b/src/middleware/client/index.ts index 0da83c7..d1fcf9e 100644 --- a/src/middleware/client/index.ts +++ b/src/middleware/client/index.ts @@ -7,7 +7,10 @@ import { extractCredentials } from '../../core/extract-credentials.js' import { readUpstreamAuth } from '../../core/read-upstream-auth.js' import { CreateSupabaseClientError, EnvError, Errors } from '../../errors.js' import { markConstructionFailure } from '../../core/parts/construction-failure.js' -import type { CreateContextClientOptions } from '../../types.js' +import type { + CreateContextClientOptions, + UntypedDatabase, +} from '../../types.js' /** * **Alpha.** Configuration for {@link withSupabaseClient} — the same @@ -97,7 +100,7 @@ const base = defineMiddleware< * @alpha * @category Middleware */ -export function withSupabaseClient( +export function withSupabaseClient( config?: WithSupabaseClientConfig, ): Entry<{ supabase: SupabaseClient }> { return base(config) as unknown as Entry<{ diff --git a/src/types.ts b/src/types.ts index 26afffe..863ff13 100644 --- a/src/types.ts +++ b/src/types.ts @@ -452,6 +452,16 @@ export interface CreateAdminClientOptions { supabaseOptions?: SupabaseClientOptions } +/** + * Default for the `Database` generic on every client-creating function and on + * {@link SupabaseContext} itself — mirrors `createClient()` from + * `@supabase/supabase-js`, so an untyped `withSupabase(...)` call type-checks + * the same way an untyped `createClient()` does. + * @category Types + */ +// eslint-disable-next-line @typescript-eslint/no-explicit-any +export type UntypedDatabase = any + /** * The Supabase context created for each authenticated request. * @@ -459,7 +469,7 @@ export interface CreateAdminClientOptions { * Identical regardless of which layer or adapter produced it. * @category Types */ -export interface SupabaseContext { +export interface SupabaseContext { /** Supabase client scoped to the caller's identity. RLS policies apply. */ supabase: SupabaseClient diff --git a/src/with-supabase.ts b/src/with-supabase.ts index 52835c7..12c1823 100644 --- a/src/with-supabase.ts +++ b/src/with-supabase.ts @@ -16,6 +16,7 @@ import { withSupabaseClient } from './middleware/client/index.js' import type { AuthModeWithKey, SupabaseContext, + UntypedDatabase, WithSupabaseConfig, } from './types.js' @@ -148,7 +149,7 @@ function rejectMiddlewareOption(config: WithSupabaseConfig): void { * ``` */ export function withSupabase< - Database = unknown, + Database = UntypedDatabase, Base extends BaseContext = BaseContext, >( config: WithSupabaseConfig, @@ -193,7 +194,7 @@ export function withSupabase< * } * ``` */ -export function withSupabase( +export function withSupabase( config: WithSupabaseConfig, ): Entry>