Skip to content

feat: reland postgrest 13 support #1537

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Aug 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
"@supabase/auth-js": "2.71.1",
"@supabase/functions-js": "2.4.5",
"@supabase/node-fetch": "2.6.15",
"@supabase/postgrest-js": "1.19.4",
"@supabase/postgrest-js": "1.21.3",
"@supabase/realtime-js": "2.15.1",
"@supabase/storage-js": "^2.10.4"
},
Expand Down
49 changes: 38 additions & 11 deletions src/SupabaseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,36 @@ import { Fetch, GenericSchema, SupabaseClientOptions, SupabaseAuthClientOptions
*/
export default class SupabaseClient<
Database = any,
SchemaName extends string & keyof Database = 'public' extends keyof Database
// The second type parameter is also used for specifying db_schema, so we
// support both cases.
// TODO: Allow setting db_schema from ClientOptions.
SchemaNameOrClientOptions extends
| (string & keyof Omit<Database, '__InternalSupabase'>)
| { PostgrestVersion: string } = 'public' extends keyof Omit<Database, '__InternalSupabase'>
? 'public'
: string & keyof Database,
Schema extends GenericSchema = Database[SchemaName] extends GenericSchema
? Database[SchemaName]
: any
: string & keyof Omit<Database, '__InternalSupabase'>,
SchemaName extends string &
keyof Omit<Database, '__InternalSupabase'> = SchemaNameOrClientOptions extends string &
keyof Omit<Database, '__InternalSupabase'>
? SchemaNameOrClientOptions
: 'public' extends keyof Omit<Database, '__InternalSupabase'>
? 'public'
: string & keyof Omit<Omit<Database, '__InternalSupabase'>, '__InternalSupabase'>,
Schema extends Omit<Database, '__InternalSupabase'>[SchemaName] extends GenericSchema
? Omit<Database, '__InternalSupabase'>[SchemaName]
: never = Omit<Database, '__InternalSupabase'>[SchemaName] extends GenericSchema
? Omit<Database, '__InternalSupabase'>[SchemaName]
: never,
ClientOptions extends { PostgrestVersion: string } = SchemaNameOrClientOptions extends string &
keyof Omit<Database, '__InternalSupabase'>
? // If the version isn't explicitly set, look for it in the __InternalSupabase object to infer the right version
Database extends { __InternalSupabase: { PostgrestVersion: string } }
? Database['__InternalSupabase']
: // otherwise default to 12
{ PostgrestVersion: '12' }
: SchemaNameOrClientOptions extends { PostgrestVersion: string }
? SchemaNameOrClientOptions
: never
> {
/**
* Supabase Auth allows you to create and manage user sessions for access to data that is secured by access policies.
Expand All @@ -51,7 +75,7 @@ export default class SupabaseClient<
protected authUrl: URL
protected storageUrl: URL
protected functionsUrl: URL
protected rest: PostgrestClient<Database, SchemaName, Schema>
protected rest: PostgrestClient<Database, ClientOptions, SchemaName>
protected storageKey: string
protected fetch?: Fetch
protected changedAccessToken?: string
Expand Down Expand Up @@ -161,16 +185,16 @@ export default class SupabaseClient<
from<
TableName extends string & keyof Schema['Tables'],
Table extends Schema['Tables'][TableName]
>(relation: TableName): PostgrestQueryBuilder<Schema, Table, TableName>
>(relation: TableName): PostgrestQueryBuilder<ClientOptions, Schema, Table, TableName>
from<ViewName extends string & keyof Schema['Views'], View extends Schema['Views'][ViewName]>(
relation: ViewName
): PostgrestQueryBuilder<Schema, View, ViewName>
): PostgrestQueryBuilder<ClientOptions, Schema, View, ViewName>
/**
* Perform a query on a table or a view.
*
* @param relation - The table or view name to query
*/
from(relation: string): PostgrestQueryBuilder<Schema, any, any> {
from(relation: string): PostgrestQueryBuilder<ClientOptions, Schema, any> {
return this.rest.from(relation)
}

Expand All @@ -182,10 +206,11 @@ export default class SupabaseClient<
*
* @param schema - The schema to query
*/
schema<DynamicSchema extends string & keyof Database>(
schema<DynamicSchema extends string & keyof Omit<Database, '__InternalSupabase'>>(
schema: DynamicSchema
): PostgrestClient<
Database,
ClientOptions,
DynamicSchema,
Database[DynamicSchema] extends GenericSchema ? Database[DynamicSchema] : any
> {
Expand Down Expand Up @@ -225,6 +250,7 @@ export default class SupabaseClient<
count?: 'exact' | 'planned' | 'estimated'
} = {}
): PostgrestFilterBuilder<
ClientOptions,
Schema,
Fn['Returns'] extends any[]
? Fn['Returns'][number] extends Record<string, unknown>
Expand All @@ -233,7 +259,8 @@ export default class SupabaseClient<
: never,
Fn['Returns'],
FnName,
null
null,
'RPC'
> {
return this.rest.rpc(fn, args, options)
}
Expand Down
26 changes: 18 additions & 8 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import SupabaseClient from './SupabaseClient'
import type { GenericSchema, SupabaseClientOptions } from './lib/types'
import type { SupabaseClientOptions } from './lib/types'

export * from '@supabase/auth-js'
export type { User as AuthUser, Session as AuthSession } from '@supabase/auth-js'
Expand All @@ -26,18 +26,28 @@ export type { SupabaseClientOptions, QueryResult, QueryData, QueryError } from '
*/
export const createClient = <
Database = any,
SchemaName extends string & keyof Database = 'public' extends keyof Database
SchemaNameOrClientOptions extends
| (string & keyof Omit<Database, '__InternalSupabase'>)
| { PostgrestVersion: string } = 'public' extends keyof Omit<Database, '__InternalSupabase'>
? 'public'
: string & keyof Database,
Schema extends GenericSchema = Database[SchemaName] extends GenericSchema
? Database[SchemaName]
: any
: string & keyof Omit<Database, '__InternalSupabase'>,
SchemaName extends string &
keyof Omit<Database, '__InternalSupabase'> = SchemaNameOrClientOptions extends string &
keyof Omit<Database, '__InternalSupabase'>
? SchemaNameOrClientOptions
: 'public' extends keyof Omit<Database, '__InternalSupabase'>
? 'public'
: string & keyof Omit<Omit<Database, '__InternalSupabase'>, '__InternalSupabase'>
>(
supabaseUrl: string,
supabaseKey: string,
options?: SupabaseClientOptions<SchemaName>
): SupabaseClient<Database, SchemaName, Schema> => {
return new SupabaseClient<Database, SchemaName, Schema>(supabaseUrl, supabaseKey, options)
): SupabaseClient<Database, SchemaNameOrClientOptions, SchemaName> => {
return new SupabaseClient<Database, SchemaNameOrClientOptions, SchemaName>(
supabaseUrl,
supabaseKey,
options
)
}

// Check for Node.js <= 18 deprecation
Expand Down
14 changes: 14 additions & 0 deletions test/integration/next/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { Metadata } from 'next'

export const metadata: Metadata = {
title: 'Supabase Integration Test',
description: 'Testing Supabase integration with Next.js',
}

export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
4 changes: 3 additions & 1 deletion test/integration/next/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
"lint": "next lint",
"test": "playwright test",
"test:ui": "playwright test --ui",
"test:debug": "playwright test --debug"
"test:debug": "playwright test --debug",
"test:types": "npx tsd --files tests/types/*.test-d.ts"
},
"dependencies": {
"@radix-ui/react-checkbox": "^1.3.1",
Expand Down Expand Up @@ -37,6 +38,7 @@
"postcss": "^8",
"tailwindcss": "^3.4.1",
"tailwindcss-animate": "^1.0.7",
"tsd": "^0.33.0",
"typescript": "^5"
}
}
180 changes: 180 additions & 0 deletions test/integration/next/tests/types/types.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import { createServerClient, createBrowserClient } from '@supabase/ssr'
import { expectType } from 'tsd'

// Copied from ts-expect
// https://github.com/TypeStrong/ts-expect/blob/master/src/index.ts#L23-L27
export type TypeEqual<Target, Value> = (<T>() => T extends Target ? 1 : 2) extends <
T
>() => T extends Value ? 1 : 2
? true
: false

type Database = {
public: {
Tables: {
shops: {
Row: {
address: string | null
id: number
shop_geom: unknown | null
}
Insert: {
address?: string | null
id: number
shop_geom?: unknown | null
}
Update: {
address?: string | null
id?: number
shop_geom?: unknown | null
}
Relationships: []
}
}
Views: {
[_ in never]: never
}
Functions: {
[_ in never]: never
}
Enums: {
[_ in never]: never
}
CompositeTypes: {
[_ in never]: never
}
}
}

{
// createBrowserClient should return a typed client
const pg12Client = createBrowserClient<Database>('HTTP://localhost:3000', '')
const res12 = await pg12Client.from('shops').select('*')
expectType<
TypeEqual<
| {
address: string | null
id: number
shop_geom: unknown | null
}[]
| null,
typeof res12.data
>
>(true)
}

{
// createBrowserClient should infer everything to any without types provided
const pg12Client = createBrowserClient('HTTP://localhost:3000', '')
const res12 = await pg12Client.from('shops').select('address, id, relation(field)')
expectType<
TypeEqual<
| {
address: any
id: any
relation: {
field: any
}[]
}[]
| null,
typeof res12.data
>
>(true)
}

{
// createServerClient should return a typed client
const pg12Server = createServerClient<Database>('HTTP://localhost:3000', '')
const res12 = await pg12Server.from('shops').select('*')
expectType<
TypeEqual<
| {
address: string | null
id: number
shop_geom: unknown | null
}[]
| null,
typeof res12.data
>
>(true)
}

{
// createServerClient should infer everything to any without types provided
const pg12Server = createServerClient('HTTP://localhost:3000', '')
const res12 = await pg12Server.from('shops').select('address, id, relation(field)')
expectType<
TypeEqual<
| {
address: any
id: any
relation: {
field: any
}[]
}[]
| null,
typeof res12.data
>
>(true)
}
// Should be able to get a PostgrestVersion 13 client from __InternalSupabase
{
type DatabaseWithInternals = {
__InternalSupabase: {
PostgrestVersion: '13'
}
public: {
Tables: {
shops: {
Row: {
address: string | null
id: number
shop_geom: unknown | null
}
Insert: {
address?: string | null
id: number
shop_geom?: unknown | null
}
Update: {
address?: string | null
id?: number
shop_geom?: unknown | null
}
Relationships: []
}
}
Views: {
[_ in never]: never
}
Functions: {
[_ in never]: never
}
Enums: {
[_ in never]: never
}
CompositeTypes: {
[_ in never]: never
}
}
}
const pg13BrowserClient = createBrowserClient<DatabaseWithInternals>('HTTP://localhost:3000', '')
const pg13ServerClient = createServerClient<DatabaseWithInternals>('HTTP://localhost:3000', '', {
cookies: { getAll: () => [], setAll: () => {} },
})
const res13 = await pg13BrowserClient.from('shops').update({ id: 21 }).maxAffected(1)
expectType<typeof res13.data>(null)
const res13Server = await pg13ServerClient.from('shops').update({ id: 21 }).maxAffected(1)
expectType<typeof res13Server.data>(null)
}
{
// Should default to PostgrestVersion 12
const pg12BrowserClient = createBrowserClient<Database>('HTTP://localhost:3000', '')
const pg12ServerClient = createServerClient<Database>('HTTP://localhost:3000', '', {
cookies: { getAll: () => [], setAll: () => {} },
})
const res12 = await pg12BrowserClient.from('shops').update({ id: 21 }).maxAffected(1)
expectType<typeof res12.Error>('maxAffected method only available on postgrest 13+')
const res12Server = await pg12ServerClient.from('shops').update({ id: 21 }).maxAffected(1)
expectType<typeof res12Server.Error>('maxAffected method only available on postgrest 13+')
}
2 changes: 1 addition & 1 deletion test/integration/next/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
"exclude": ["node_modules", "test/types/*.test-d.ts"]
}
Loading
Loading