Skip to content

Commit

Permalink
feat: postgrest-js@1
Browse files Browse the repository at this point in the history
  • Loading branch information
soedirgo committed Jun 20, 2022
1 parent 801bfd1 commit f0a3691
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 65 deletions.
18 changes: 9 additions & 9 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
Expand Up @@ -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"
Expand Down
88 changes: 50 additions & 38 deletions src/SupabaseClient.ts
@@ -1,14 +1,18 @@
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'
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',
Expand All @@ -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<Database, SchemaName>
protected multiTab: boolean
protected fetch?: Fetch
protected changedAccessToken: string | undefined
Expand All @@ -62,15 +73,14 @@ export default class SupabaseClient {
constructor(
protected supabaseUrl: string,
protected supabaseKey: string,
options?: SupabaseClientOptions
options?: SupabaseClientOptions<SchemaName>
) {
if (!supabaseUrl) throw new Error('supabaseUrl is required.')
if (!supabaseKey) throw new Error('supabaseKey is required.')

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`
Expand All @@ -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()
Expand Down Expand Up @@ -124,14 +139,11 @@ export default class SupabaseClient {
*
* @param table The table name to operate on.
*/
from<T = any>(table: string): PostgrestQueryBuilder<T> {
const url = `${this.restUrl}/${table}`
return new PostgrestQueryBuilder<T>(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<Table> {
return this.rest.from(table)
}

/**
Expand All @@ -143,16 +155,25 @@ export default class SupabaseClient {
* @param count Count algorithm to use to count rows in a table.
*
*/
rpc<T = any>(
fn: string,
params?: object,
{
head = false,
count = null,
}: { head?: boolean; count?: null | 'exact' | 'planned' | 'estimated' } = {}
) {
const rest = this._initPostgRESTClient()
return rest.rpc<T>(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<string, unknown>
? Function_['Returns'][number]
: never
: never,
Function_['Returns']
> {
return this.rest.rpc(fn, args, options)
}

/**
Expand Down Expand Up @@ -249,7 +270,7 @@ export default class SupabaseClient {
fetch,
cookieOptions,
multiTab,
}: SupabaseClientOptions) {
}: SupabaseClientOptions<string>) {
const authHeaders = {
Authorization: `Bearer ${this.supabaseKey}`,
apikey: `${this.supabaseKey}`,
Expand All @@ -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
Expand Down
32 changes: 17 additions & 15 deletions 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<SchemaName>
): SupabaseClient<Database, SchemaName, Schema> => {
return new SupabaseClient(supabaseUrl, supabaseKey, options)
}

export {
createClient,
SupabaseClient,
SupabaseClientOptions,
SupabaseRealtimePayload,
AuthUser,
AuthSession,
}
20 changes: 18 additions & 2 deletions src/lib/types.ts
Expand Up @@ -7,11 +7,11 @@ export interface SupabaseAuthClientOptions extends GoTrueClientOptions {}

export type Fetch = typeof fetch

export type SupabaseClientOptions = {
export type SupabaseClientOptions<SchemaName> = {
/**
* 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.
*/
Expand Down Expand Up @@ -69,3 +69,19 @@ export type SupabaseRealtimePayload<T> = {
old: T
errors: string[] | null
}

export type GenericTable = {
Row: Record<string, unknown>
Insert: Record<string, unknown>
Update: Record<string, unknown>
}

export type GenericFunction = {
Args: Record<string, unknown>
Returns: unknown
}

export type GenericSchema = {
Tables: Record<string, GenericTable>
Functions: Record<string, GenericFunction>
}

0 comments on commit f0a3691

Please sign in to comment.