From f0a3691b5aed80cb13213fc9578c5b3c9c46ffa2 Mon Sep 17 00:00:00 2001 From: Bobbie Soedirgo Date: Mon, 20 Jun 2022 16:50:16 +0800 Subject: [PATCH] feat: postgrest-js@1 --- package-lock.json | 18 ++++----- package.json | 2 +- src/SupabaseClient.ts | 88 ++++++++++++++++++++++++------------------- src/index.ts | 32 ++++++++-------- src/lib/types.ts | 20 +++++++++- 5 files changed, 95 insertions(+), 65 deletions(-) diff --git a/package-lock.json b/package-lock.json index e901ac58..420ee7ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "@supabase/functions-js": "^1.3.3", "@supabase/gotrue-js": "^1.23.0-next.3", - "@supabase/postgrest-js": "^0.37.2", + "@supabase/postgrest-js": "^1.0.0-next.2", "@supabase/realtime-js": "^1.7.2", "@supabase/storage-js": "^1.7.0", "cross-fetch": "^3.1.5" @@ -927,11 +927,11 @@ } }, "node_modules/@supabase/postgrest-js": { - "version": "0.37.2", - "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-0.37.2.tgz", - "integrity": "sha512-3Dgx5k3RvtKqc8DvR2BEyh2fVyjZe5P4e0zD1r8dyuVmpaYDaASZ2YeNVgyWXMCWH7xzrj4vepTYlKwfj78QLg==", + "version": "1.0.0-next.2", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.0.0-next.2.tgz", + "integrity": "sha512-8hg7xbT6w07BjrQRqQT4DOVg9NkcBFdvlpdArriKwOtqKiztVy32HXT/CjPTgMxl9Z4Bc7n2krhS5EKOB7Wmvw==", "dependencies": { - "cross-fetch": "^3.0.6" + "cross-fetch": "^3.1.5" } }, "node_modules/@supabase/realtime-js": { @@ -9105,11 +9105,11 @@ } }, "@supabase/postgrest-js": { - "version": "0.37.2", - "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-0.37.2.tgz", - "integrity": "sha512-3Dgx5k3RvtKqc8DvR2BEyh2fVyjZe5P4e0zD1r8dyuVmpaYDaASZ2YeNVgyWXMCWH7xzrj4vepTYlKwfj78QLg==", + "version": "1.0.0-next.2", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.0.0-next.2.tgz", + "integrity": "sha512-8hg7xbT6w07BjrQRqQT4DOVg9NkcBFdvlpdArriKwOtqKiztVy32HXT/CjPTgMxl9Z4Bc7n2krhS5EKOB7Wmvw==", "requires": { - "cross-fetch": "^3.0.6" + "cross-fetch": "^3.1.5" } }, "@supabase/realtime-js": { diff --git a/package.json b/package.json index 2435042e..a3013f3a 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "dependencies": { "@supabase/functions-js": "^1.3.3", "@supabase/gotrue-js": "^1.23.0-next.3", - "@supabase/postgrest-js": "^0.37.2", + "@supabase/postgrest-js": "^1.0.0-next.2", "@supabase/realtime-js": "^1.7.2", "@supabase/storage-js": "^1.7.0", "cross-fetch": "^3.1.5" diff --git a/src/SupabaseClient.ts b/src/SupabaseClient.ts index d78e8dd9..4f279c7b 100644 --- a/src/SupabaseClient.ts +++ b/src/SupabaseClient.ts @@ -1,6 +1,10 @@ import { FunctionsClient } from '@supabase/functions-js' import { AuthChangeEvent } from '@supabase/gotrue-js' -import { PostgrestClient, PostgrestQueryBuilder } from '@supabase/postgrest-js' +import { + PostgrestClient, + PostgrestFilterBuilder, + PostgrestQueryBuilder, +} from '@supabase/postgrest-js' import { RealtimeChannel, RealtimeClient, RealtimeClientOptions } from '@supabase/realtime-js' import { SupabaseStorageClient } from '@supabase/storage-js' import { DEFAULT_HEADERS, STORAGE_KEY } from './lib/constants' @@ -8,7 +12,7 @@ import { fetchWithAuth } from './lib/fetch' import { isBrowser, stripTrailingSlash } from './lib/helpers' import { SupabaseAuthClient } from './lib/SupabaseAuthClient' import { SupabaseRealtimeClient } from './lib/SupabaseRealtimeClient' -import { Fetch, SupabaseClientOptions } from './lib/types' +import { Fetch, GenericSchema, SupabaseClientOptions } from './lib/types' const DEFAULT_OPTIONS = { schema: 'public', @@ -24,19 +28,26 @@ const DEFAULT_OPTIONS = { * * An isomorphic Javascript client for interacting with Postgres. */ -export default class SupabaseClient { +export default class SupabaseClient< + 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 +> { /** * Supabase Auth allows you to create and manage user sessions for access to data that is secured by access policies. */ auth: SupabaseAuthClient - protected schema: string - protected restUrl: string protected realtimeUrl: string protected authUrl: string protected storageUrl: string protected functionsUrl: string protected realtime: RealtimeClient + protected rest: PostgrestClient protected multiTab: boolean protected fetch?: Fetch protected changedAccessToken: string | undefined @@ -62,7 +73,7 @@ export default class SupabaseClient { constructor( protected supabaseUrl: string, protected supabaseKey: string, - options?: SupabaseClientOptions + options?: SupabaseClientOptions ) { if (!supabaseUrl) throw new Error('supabaseUrl is required.') if (!supabaseKey) throw new Error('supabaseKey is required.') @@ -70,7 +81,6 @@ export default class SupabaseClient { const _supabaseUrl = stripTrailingSlash(supabaseUrl) const settings = { ...DEFAULT_OPTIONS, ...options } - this.restUrl = `${_supabaseUrl}/rest/v1` this.realtimeUrl = `${_supabaseUrl}/realtime/v1`.replace('http', 'ws') this.authUrl = `${_supabaseUrl}/auth/v1` this.storageUrl = `${_supabaseUrl}/storage/v1` @@ -83,15 +93,20 @@ export default class SupabaseClient { this.functionsUrl = `${_supabaseUrl}/functions/v1` } - this.schema = settings.schema this.multiTab = settings.multiTab this.headers = { ...DEFAULT_HEADERS, ...options?.headers } this.shouldThrowOnError = settings.shouldThrowOnError || false + this.fetch = fetchWithAuth(supabaseKey, this._getAccessToken.bind(this), settings.fetch) + this.auth = this._initSupabaseAuthClient(settings) this.realtime = this._initRealtimeClient({ headers: this.headers, ...settings.realtime }) - - this.fetch = fetchWithAuth(supabaseKey, this._getAccessToken.bind(this), settings.fetch) + this.rest = new PostgrestClient(`${_supabaseUrl}/rest/v1`, { + headers: this.headers, + schema: options?.schema, + fetch: this.fetch, + throwOnError: this.shouldThrowOnError, + }) this._listenForAuthEvents() this._listenForMultiTabEvents() @@ -124,14 +139,11 @@ export default class SupabaseClient { * * @param table The table name to operate on. */ - from(table: string): PostgrestQueryBuilder { - const url = `${this.restUrl}/${table}` - return new PostgrestQueryBuilder(url, { - headers: this.headers, - schema: this.schema, - fetch: this.fetch, - shouldThrowOnError: this.shouldThrowOnError, - }) + from< + TableName extends string & keyof Schema['Tables'], + Table extends Schema['Tables'][TableName] + >(table: TableName): PostgrestQueryBuilder { + return this.rest.from(table) } /** @@ -143,16 +155,25 @@ export default class SupabaseClient { * @param count Count algorithm to use to count rows in a table. * */ - rpc( - fn: string, - params?: object, - { - head = false, - count = null, - }: { head?: boolean; count?: null | 'exact' | 'planned' | 'estimated' } = {} - ) { - const rest = this._initPostgRESTClient() - return rest.rpc(fn, params, { head, count }) + rpc< + FunctionName extends string & keyof Schema['Functions'], + Function_ extends Schema['Functions'][FunctionName] + >( + fn: FunctionName, + args: Function_['Args'] = {}, + options?: { + head?: boolean + count?: 'exact' | 'planned' | 'estimated' + } + ): PostgrestFilterBuilder< + Function_['Returns'] extends any[] + ? Function_['Returns'][number] extends Record + ? Function_['Returns'][number] + : never + : never, + Function_['Returns'] + > { + return this.rest.rpc(fn, args, options) } /** @@ -249,7 +270,7 @@ export default class SupabaseClient { fetch, cookieOptions, multiTab, - }: SupabaseClientOptions) { + }: SupabaseClientOptions) { const authHeaders = { Authorization: `Bearer ${this.supabaseKey}`, apikey: `${this.supabaseKey}`, @@ -274,15 +295,6 @@ export default class SupabaseClient { }) } - private _initPostgRESTClient() { - return new PostgrestClient(this.restUrl, { - headers: this.headers, - schema: this.schema, - fetch: this.fetch, - throwOnError: this.shouldThrowOnError, - }) - } - private _listenForMultiTabEvents() { if (!this.multiTab || !isBrowser() || !window?.addEventListener) { return null diff --git a/src/index.ts b/src/index.ts index 4cb6753c..06fb0bff 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,31 +1,33 @@ import SupabaseClient from './SupabaseClient' -import { SupabaseClientOptions, SupabaseRealtimePayload } from './lib/types' -import { User as AuthUser, Session as AuthSession } from '@supabase/gotrue-js' +import type { GenericSchema, SupabaseClientOptions } from './lib/types' + export * from '@supabase/gotrue-js' -export { +export type { User as AuthUser, Session as AuthSession } from '@supabase/gotrue-js' +export type { PostgrestResponse, PostgrestSingleResponse, PostgrestMaybeSingleResponse, PostgrestError, } from '@supabase/postgrest-js' export * from '@supabase/realtime-js' +export { default as SupabaseClient } from './SupabaseClient' +export type { SupabaseClientOptions, SupabaseRealtimePayload } from './lib/types' /** * Creates a new Supabase Client. */ -const createClient = ( +export const createClient = < + 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 +>( supabaseUrl: string, supabaseKey: string, - options?: SupabaseClientOptions -): SupabaseClient => { + options?: SupabaseClientOptions +): SupabaseClient => { return new SupabaseClient(supabaseUrl, supabaseKey, options) } - -export { - createClient, - SupabaseClient, - SupabaseClientOptions, - SupabaseRealtimePayload, - AuthUser, - AuthSession, -} diff --git a/src/lib/types.ts b/src/lib/types.ts index 52530783..adc66d4f 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -7,11 +7,11 @@ export interface SupabaseAuthClientOptions extends GoTrueClientOptions {} export type Fetch = typeof fetch -export type SupabaseClientOptions = { +export type SupabaseClientOptions = { /** * The Postgres schema which your tables belong to. Must be on the list of exposed schemas in Supabase. Defaults to 'public'. */ - schema?: string + schema?: SchemaName /** * Optional headers for initializing the client. */ @@ -69,3 +69,19 @@ export type SupabaseRealtimePayload = { old: T errors: string[] | null } + +export type GenericTable = { + Row: Record + Insert: Record + Update: Record +} + +export type GenericFunction = { + Args: Record + Returns: unknown +} + +export type GenericSchema = { + Tables: Record + Functions: Record +}