diff --git a/src/PostgrestBuilder.ts b/src/PostgrestBuilder.ts index c1529e91..37f272e3 100644 --- a/src/PostgrestBuilder.ts +++ b/src/PostgrestBuilder.ts @@ -1,9 +1,25 @@ import crossFetch from 'cross-fetch' +import type PostgrestFilterBuilder from './PostgrestFilterBuilder' +import type PostgrestQueryBuilder from './PostgrestQueryBuilder' +import type PostgrestTransformBuilder from './PostgrestTransformBuilder' import type { Fetch, PostgrestResponse } from './types' -export default abstract class PostgrestBuilder - implements PromiseLike> +type EnableThrowOnError = T extends PostgrestFilterBuilder< + infer Schema, + infer Row, + infer Result, + any +> + ? PostgrestFilterBuilder + : T extends PostgrestTransformBuilder + ? PostgrestTransformBuilder + : T extends PostgrestQueryBuilder + ? PostgrestQueryBuilder + : any + +export default abstract class PostgrestBuilder + implements PromiseLike> { protected method: 'GET' | 'HEAD' | 'POST' | 'PATCH' | 'DELETE' protected url: URL @@ -15,7 +31,7 @@ export default abstract class PostgrestBuilder protected fetch: Fetch protected allowEmpty: boolean - constructor(builder: PostgrestBuilder) { + constructor(builder: PostgrestBuilder) { this.method = builder.method this.url = builder.url this.headers = builder.headers @@ -40,14 +56,14 @@ export default abstract class PostgrestBuilder * * {@link https://github.com/supabase/supabase-js/issues/92} */ - throwOnError(): this { + throwOnError(): EnableThrowOnError { this.shouldThrowOnError = true - return this + return this as any } - then, TResult2 = never>( + then, TResult2 = never>( onfulfilled?: - | ((value: PostgrestResponse) => TResult1 | PromiseLike) + | ((value: PostgrestResponse) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null diff --git a/src/PostgrestClient.ts b/src/PostgrestClient.ts index 2e14a12f..911ec90c 100644 --- a/src/PostgrestClient.ts +++ b/src/PostgrestClient.ts @@ -63,15 +63,18 @@ export default class PostgrestClient< */ from< TableName extends string & keyof Schema['Tables'], - Table extends Schema['Tables'][TableName] - >(relation: TableName): PostgrestQueryBuilder - from( - relation: ViewName - ): PostgrestQueryBuilder - from(relation: string): PostgrestQueryBuilder - from(relation: string): PostgrestQueryBuilder { + Table extends Schema['Tables'][TableName], + ThrowOnError + >(relation: TableName): PostgrestQueryBuilder + from< + ViewName extends string & keyof Schema['Views'], + View extends Schema['Views'][ViewName], + ThrowOnError + >(relation: ViewName): PostgrestQueryBuilder + from(relation: string): PostgrestQueryBuilder + from(relation: string): PostgrestQueryBuilder { const url = new URL(`${this.url}/${relation}`) - return new PostgrestQueryBuilder(url, { + return new PostgrestQueryBuilder(url, { headers: { ...this.headers }, schema: this.schema, fetch: this.fetch, @@ -101,7 +104,8 @@ export default class PostgrestClient< */ rpc< FunctionName extends string & keyof Schema['Functions'], - Function_ extends Schema['Functions'][FunctionName] + Function_ extends Schema['Functions'][FunctionName], + ThrowOnError >( fn: FunctionName, args: Function_['Args'] = {}, @@ -119,7 +123,8 @@ export default class PostgrestClient< ? Function_['Returns'][number] : never : never, - Function_['Returns'] + Function_['Returns'], + ThrowOnError > { let method: 'HEAD' | 'POST' const url = new URL(`${this.url}/rpc/${fn}`) @@ -147,6 +152,6 @@ export default class PostgrestClient< body, fetch: this.fetch, allowEmpty: false, - } as unknown as PostgrestBuilder) + } as unknown as PostgrestBuilder) } } diff --git a/src/PostgrestFilterBuilder.ts b/src/PostgrestFilterBuilder.ts index bab03de5..b052ba57 100644 --- a/src/PostgrestFilterBuilder.ts +++ b/src/PostgrestFilterBuilder.ts @@ -28,8 +28,9 @@ type FilterOperator = export default class PostgrestFilterBuilder< Schema extends GenericSchema, Row extends Record, - Result -> extends PostgrestTransformBuilder { + Result, + ThrowOnError +> extends PostgrestTransformBuilder { /** * Match only rows where `column` is equal to `value`. * diff --git a/src/PostgrestQueryBuilder.ts b/src/PostgrestQueryBuilder.ts index d25966f9..120e728a 100644 --- a/src/PostgrestQueryBuilder.ts +++ b/src/PostgrestQueryBuilder.ts @@ -5,7 +5,8 @@ import { Fetch, GenericSchema, GenericTable, GenericView } from './types' export default class PostgrestQueryBuilder< Schema extends GenericSchema, - Relation extends GenericTable | GenericView + Relation extends GenericTable | GenericView, + ThrowOnError > { url: URL headers: Record @@ -52,10 +53,7 @@ export default class PostgrestQueryBuilder< * `"estimated"`: Uses exact count for low numbers and planned count for high * numbers. */ - select< - Query extends string = '*', - Result = GetResult - >( + select>( columns?: Query, { head = false, @@ -64,7 +62,7 @@ export default class PostgrestQueryBuilder< head?: boolean count?: 'exact' | 'planned' | 'estimated' } = {} - ): PostgrestFilterBuilder { + ): PostgrestFilterBuilder { const method = head ? 'HEAD' : 'GET' // Remove whitespaces except when quoted let quoted = false @@ -92,7 +90,7 @@ export default class PostgrestQueryBuilder< schema: this.schema, fetch: this.fetch, allowEmpty: false, - } as unknown as PostgrestBuilder) + } as unknown as PostgrestBuilder) } /** @@ -124,7 +122,7 @@ export default class PostgrestQueryBuilder< }: { count?: 'exact' | 'planned' | 'estimated' } = {} - ): PostgrestFilterBuilder { + ): PostgrestFilterBuilder { const method = 'POST' const prefersHeaders = [] @@ -153,7 +151,7 @@ export default class PostgrestQueryBuilder< body, fetch: this.fetch, allowEmpty: false, - } as unknown as PostgrestBuilder) + } as unknown as PostgrestBuilder) } /** @@ -200,7 +198,7 @@ export default class PostgrestQueryBuilder< ignoreDuplicates?: boolean count?: 'exact' | 'planned' | 'estimated' } = {} - ): PostgrestFilterBuilder { + ): PostgrestFilterBuilder { const method = 'POST' const prefersHeaders = [`resolution=${ignoreDuplicates ? 'ignore' : 'merge'}-duplicates`] @@ -223,7 +221,7 @@ export default class PostgrestQueryBuilder< body, fetch: this.fetch, allowEmpty: false, - } as unknown as PostgrestBuilder) + } as unknown as PostgrestBuilder) } /** @@ -254,7 +252,7 @@ export default class PostgrestQueryBuilder< }: { count?: 'exact' | 'planned' | 'estimated' } = {} - ): PostgrestFilterBuilder { + ): PostgrestFilterBuilder { const method = 'PATCH' const prefersHeaders = [] const body = values @@ -274,7 +272,7 @@ export default class PostgrestQueryBuilder< body, fetch: this.fetch, allowEmpty: false, - } as unknown as PostgrestBuilder) + } as unknown as PostgrestBuilder) } /** @@ -300,7 +298,7 @@ export default class PostgrestQueryBuilder< count, }: { count?: 'exact' | 'planned' | 'estimated' - } = {}): PostgrestFilterBuilder { + } = {}): PostgrestFilterBuilder { const method = 'DELETE' const prefersHeaders = [] if (count) { @@ -318,6 +316,6 @@ export default class PostgrestQueryBuilder< schema: this.schema, fetch: this.fetch, allowEmpty: false, - } as unknown as PostgrestBuilder) + } as unknown as PostgrestBuilder) } } diff --git a/src/PostgrestTransformBuilder.ts b/src/PostgrestTransformBuilder.ts index c4f97812..78685879 100644 --- a/src/PostgrestTransformBuilder.ts +++ b/src/PostgrestTransformBuilder.ts @@ -10,8 +10,9 @@ import { export default class PostgrestTransformBuilder< Schema extends GenericSchema, Row extends Record, - Result -> extends PostgrestBuilder { + Result, + ThrowOnError +> extends PostgrestBuilder { /** * Perform a SELECT on the query result. * @@ -23,7 +24,7 @@ export default class PostgrestTransformBuilder< */ select>( columns?: Query - ): PostgrestTransformBuilder { + ): PostgrestTransformBuilder { // Remove whitespaces except when quoted let quoted = false const cleanedColumns = (columns ?? '*') @@ -43,7 +44,7 @@ export default class PostgrestTransformBuilder< this.headers['Prefer'] += ',' } this.headers['Prefer'] += 'return=representation' - return this as unknown as PostgrestTransformBuilder + return this as unknown as PostgrestTransformBuilder } /** @@ -138,9 +139,9 @@ export default class PostgrestTransformBuilder< * Query result must be one row (e.g. using `.limit(1)`), otherwise this * returns an error. */ - single(): PromiseLike> { + single(): PromiseLike> { this.headers['Accept'] = 'application/vnd.pgrst.object+json' - return this as PromiseLike> + return this as unknown as PromiseLike> } /** @@ -149,26 +150,28 @@ export default class PostgrestTransformBuilder< * Query result must be zero or one row (e.g. using `.limit(1)`), otherwise * this returns an error. */ - maybeSingle(): PromiseLike> { + maybeSingle(): PromiseLike> { this.headers['Accept'] = 'application/vnd.pgrst.object+json' this.allowEmpty = true - return this as PromiseLike> + return this as unknown as PromiseLike> } /** * Return `data` as a string in CSV format. */ - csv(): PromiseLike> { + csv(): PromiseLike> { this.headers['Accept'] = 'text/csv' - return this as PromiseLike> + return this as unknown as PromiseLike> } /** * Return `data` as an object in [GeoJSON](https://geojson.org) format. */ - geojson(): PromiseLike>> { + geojson(): PromiseLike, ThrowOnError>> { this.headers['Accept'] = 'application/geo+json' - return this as PromiseLike>> + return this as unknown as PromiseLike< + PostgrestSingleResponse, ThrowOnError> + > } /** @@ -207,8 +210,8 @@ export default class PostgrestTransformBuilder< wal?: boolean format?: 'json' | 'text' } = {}): - | PromiseLike>> - | PromiseLike> { + | PromiseLike, ThrowOnError>> + | PromiseLike> { const options = [ analyze ? 'analyze' : null, verbose ? 'verbose' : null, @@ -223,8 +226,9 @@ export default class PostgrestTransformBuilder< this.headers[ 'Accept' ] = `application/vnd.pgrst.plan+${format}; for="${forMediatype}"; options=${options};` - if (format === 'json') return this as PromiseLike>> - else return this as PromiseLike> + if (format === 'json') + return this as PromiseLike, ThrowOnError>> + else return this as unknown as PromiseLike> } /** @@ -246,7 +250,7 @@ export default class PostgrestTransformBuilder< * * @typeParam NewResult - The new result type to override with */ - returns(): PostgrestTransformBuilder { - return this as unknown as PostgrestTransformBuilder + returns(): PostgrestTransformBuilder { + return this as unknown as PostgrestTransformBuilder } } diff --git a/src/types.ts b/src/types.ts index 07cc656f..0277a6ff 100644 --- a/src/types.ts +++ b/src/types.ts @@ -32,17 +32,22 @@ interface PostgrestResponseFailure extends PostgrestResponseBase { data: null count: null } -export type PostgrestResponse = PostgrestResponseSuccess | PostgrestResponseFailure +export type PostgrestResponse = ThrowOnError extends true + ? PostgrestResponseSuccess + : PostgrestResponseSuccess | PostgrestResponseFailure interface PostgrestSingleResponseSuccess extends PostgrestResponseBase { error: null data: T count: number | null } -export type PostgrestSingleResponse = - | PostgrestSingleResponseSuccess - | PostgrestResponseFailure -export type PostgrestMaybeSingleResponse = PostgrestSingleResponse +export type PostgrestSingleResponse = ThrowOnError extends true + ? PostgrestSingleResponseSuccess + : PostgrestSingleResponseSuccess | PostgrestResponseFailure +export type PostgrestMaybeSingleResponse = PostgrestSingleResponse< + T | null, + ThrowOnError +> export type GenericTable = { Row: Record