Skip to content

feat(types): add create client helper type #1526

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

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
22 changes: 12 additions & 10 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,25 @@ export * from '@supabase/realtime-js'
export { default as SupabaseClient } from './SupabaseClient'
export type { SupabaseClientOptions, QueryResult, QueryData, QueryError } from './lib/types'

/**
* Creates a new Supabase Client.
*/
export const createClient = <
export type CreateClientHelper<AdditionalOptions = {}> = <
Database = any,
SchemaName extends string & keyof Database = 'public' extends keyof Database
? 'public'
: string & keyof Database,
Schema extends GenericSchema = Database[SchemaName] extends GenericSchema
? Database[SchemaName]
: any
Schema = Database[SchemaName] extends GenericSchema ? Database[SchemaName] : any
>(
supabaseUrl: string,
supabaseKey: string,
options?: SupabaseClientOptions<SchemaName>
): SupabaseClient<Database, SchemaName, Schema> => {
return new SupabaseClient<Database, SchemaName, Schema>(supabaseUrl, supabaseKey, options)
options?: SupabaseClientOptions<SchemaName> & AdditionalOptions
) => SupabaseClient<Database, SchemaName, Schema extends GenericSchema ? Schema : any>

export type GenericSupabaseClient = SupabaseClient<any, any, any>

/**
* Creates a new Supabase Client.
*/
export const createClient: CreateClientHelper = (supabaseUrl, supabaseKey, options) => {
return new SupabaseClient(supabaseUrl, supabaseKey, options)
}

// Check for Node.js <= 18 deprecation
Expand Down
1 change: 1 addition & 0 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,4 @@ export type GenericSchema = {
export type QueryResult<T> = T extends PromiseLike<infer U> ? U : never
export type QueryData<T> = T extends PromiseLike<{ data: infer U }> ? Exclude<U, null> : never
export type QueryError = PostgrestError
export type ServicesOptions = {}
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"
}
}
119 changes: 119 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,119 @@
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)
}
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