Skip to content
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

Improve return types for throwOnError #364

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
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
30 changes: 23 additions & 7 deletions src/PostgrestBuilder.ts
Original file line number Diff line number Diff line change
@@ -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<Result>
implements PromiseLike<PostgrestResponse<Result>>
type EnableThrowOnError<T> = T extends PostgrestFilterBuilder<
infer Schema,
infer Row,
infer Result,
any
>
? PostgrestFilterBuilder<Schema, Row, Result, true>
: T extends PostgrestTransformBuilder<infer Schema, infer Row, infer Result, any>
? PostgrestTransformBuilder<Schema, Row, Result, true>
: T extends PostgrestQueryBuilder<infer Schema, infer Relation, any>
? PostgrestQueryBuilder<Schema, Relation, true>
: any

export default abstract class PostgrestBuilder<Result, ThrowOnError>
implements PromiseLike<PostgrestResponse<Result, ThrowOnError>>
{
protected method: 'GET' | 'HEAD' | 'POST' | 'PATCH' | 'DELETE'
protected url: URL
Expand All @@ -15,7 +31,7 @@ export default abstract class PostgrestBuilder<Result>
protected fetch: Fetch
protected allowEmpty: boolean

constructor(builder: PostgrestBuilder<Result>) {
constructor(builder: PostgrestBuilder<Result, ThrowOnError>) {
this.method = builder.method
this.url = builder.url
this.headers = builder.headers
Expand All @@ -40,14 +56,14 @@ export default abstract class PostgrestBuilder<Result>
*
* {@link https://github.com/supabase/supabase-js/issues/92}
*/
throwOnError(): this {
throwOnError(): EnableThrowOnError<this> {
this.shouldThrowOnError = true
return this
return this as any
}

then<TResult1 = PostgrestResponse<Result>, TResult2 = never>(
then<TResult1 = PostgrestResponse<Result, ThrowOnError>, TResult2 = never>(
onfulfilled?:
| ((value: PostgrestResponse<Result>) => TResult1 | PromiseLike<TResult1>)
| ((value: PostgrestResponse<Result, ThrowOnError>) => TResult1 | PromiseLike<TResult1>)
| undefined
| null,
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
Expand Down
27 changes: 16 additions & 11 deletions src/PostgrestClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,18 @@ export default class PostgrestClient<
*/
from<
TableName extends string & keyof Schema['Tables'],
Table extends Schema['Tables'][TableName]
>(relation: TableName): PostgrestQueryBuilder<Schema, Table>
from<ViewName extends string & keyof Schema['Views'], View extends Schema['Views'][ViewName]>(
relation: ViewName
): PostgrestQueryBuilder<Schema, View>
from(relation: string): PostgrestQueryBuilder<Schema, any>
from(relation: string): PostgrestQueryBuilder<Schema, any> {
Table extends Schema['Tables'][TableName],
ThrowOnError
>(relation: TableName): PostgrestQueryBuilder<Schema, Table, ThrowOnError>
from<
ViewName extends string & keyof Schema['Views'],
View extends Schema['Views'][ViewName],
ThrowOnError
>(relation: ViewName): PostgrestQueryBuilder<Schema, View, ThrowOnError>
from(relation: string): PostgrestQueryBuilder<Schema, any, false>
from(relation: string): PostgrestQueryBuilder<Schema, any, false> {
const url = new URL(`${this.url}/${relation}`)
return new PostgrestQueryBuilder<Schema, any>(url, {
return new PostgrestQueryBuilder<Schema, any, false>(url, {
headers: { ...this.headers },
schema: this.schema,
fetch: this.fetch,
Expand Down Expand Up @@ -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'] = {},
Expand All @@ -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}`)
Expand Down Expand Up @@ -147,6 +152,6 @@ export default class PostgrestClient<
body,
fetch: this.fetch,
allowEmpty: false,
} as unknown as PostgrestBuilder<Function_['Returns']>)
} as unknown as PostgrestBuilder<Function_['Returns'], ThrowOnError>)
}
}
5 changes: 3 additions & 2 deletions src/PostgrestFilterBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ type FilterOperator =
export default class PostgrestFilterBuilder<
Schema extends GenericSchema,
Row extends Record<string, unknown>,
Result
> extends PostgrestTransformBuilder<Schema, Row, Result> {
Result,
ThrowOnError
> extends PostgrestTransformBuilder<Schema, Row, Result, ThrowOnError> {
/**
* Match only rows where `column` is equal to `value`.
*
Expand Down
28 changes: 13 additions & 15 deletions src/PostgrestQueryBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, string>
Expand Down Expand Up @@ -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<Schema, Relation['Row'], Query>
>(
select<Query extends string = '*', Result = GetResult<Schema, Relation['Row'], Query>>(
columns?: Query,
{
head = false,
Expand All @@ -64,7 +62,7 @@ export default class PostgrestQueryBuilder<
head?: boolean
count?: 'exact' | 'planned' | 'estimated'
} = {}
): PostgrestFilterBuilder<Schema, Relation['Row'], Result> {
): PostgrestFilterBuilder<Schema, Relation['Row'], Result, ThrowOnError> {
const method = head ? 'HEAD' : 'GET'
// Remove whitespaces except when quoted
let quoted = false
Expand Down Expand Up @@ -92,7 +90,7 @@ export default class PostgrestQueryBuilder<
schema: this.schema,
fetch: this.fetch,
allowEmpty: false,
} as unknown as PostgrestBuilder<Result>)
} as unknown as PostgrestBuilder<Result, ThrowOnError>)
}

/**
Expand Down Expand Up @@ -124,7 +122,7 @@ export default class PostgrestQueryBuilder<
}: {
count?: 'exact' | 'planned' | 'estimated'
} = {}
): PostgrestFilterBuilder<Schema, Relation['Row'], undefined> {
): PostgrestFilterBuilder<Schema, Relation['Row'], undefined, ThrowOnError> {
const method = 'POST'

const prefersHeaders = []
Expand Down Expand Up @@ -153,7 +151,7 @@ export default class PostgrestQueryBuilder<
body,
fetch: this.fetch,
allowEmpty: false,
} as unknown as PostgrestBuilder<undefined>)
} as unknown as PostgrestBuilder<undefined, ThrowOnError>)
}

/**
Expand Down Expand Up @@ -200,7 +198,7 @@ export default class PostgrestQueryBuilder<
ignoreDuplicates?: boolean
count?: 'exact' | 'planned' | 'estimated'
} = {}
): PostgrestFilterBuilder<Schema, Relation['Row'], undefined> {
): PostgrestFilterBuilder<Schema, Relation['Row'], undefined, ThrowOnError> {
const method = 'POST'

const prefersHeaders = [`resolution=${ignoreDuplicates ? 'ignore' : 'merge'}-duplicates`]
Expand All @@ -223,7 +221,7 @@ export default class PostgrestQueryBuilder<
body,
fetch: this.fetch,
allowEmpty: false,
} as unknown as PostgrestBuilder<undefined>)
} as unknown as PostgrestBuilder<undefined, ThrowOnError>)
}

/**
Expand Down Expand Up @@ -254,7 +252,7 @@ export default class PostgrestQueryBuilder<
}: {
count?: 'exact' | 'planned' | 'estimated'
} = {}
): PostgrestFilterBuilder<Schema, Relation['Row'], undefined> {
): PostgrestFilterBuilder<Schema, Relation['Row'], undefined, ThrowOnError> {
const method = 'PATCH'
const prefersHeaders = []
const body = values
Expand All @@ -274,7 +272,7 @@ export default class PostgrestQueryBuilder<
body,
fetch: this.fetch,
allowEmpty: false,
} as unknown as PostgrestBuilder<undefined>)
} as unknown as PostgrestBuilder<undefined, ThrowOnError>)
}

/**
Expand All @@ -300,7 +298,7 @@ export default class PostgrestQueryBuilder<
count,
}: {
count?: 'exact' | 'planned' | 'estimated'
} = {}): PostgrestFilterBuilder<Schema, Relation['Row'], undefined> {
} = {}): PostgrestFilterBuilder<Schema, Relation['Row'], undefined, ThrowOnError> {
const method = 'DELETE'
const prefersHeaders = []
if (count) {
Expand All @@ -318,6 +316,6 @@ export default class PostgrestQueryBuilder<
schema: this.schema,
fetch: this.fetch,
allowEmpty: false,
} as unknown as PostgrestBuilder<undefined>)
} as unknown as PostgrestBuilder<undefined, ThrowOnError>)
}
}
40 changes: 22 additions & 18 deletions src/PostgrestTransformBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import {
export default class PostgrestTransformBuilder<
Schema extends GenericSchema,
Row extends Record<string, unknown>,
Result
> extends PostgrestBuilder<Result> {
Result,
ThrowOnError
> extends PostgrestBuilder<Result, ThrowOnError> {
/**
* Perform a SELECT on the query result.
*
Expand All @@ -23,7 +24,7 @@ export default class PostgrestTransformBuilder<
*/
select<Query extends string = '*', NewResult = GetResult<Schema, Row, Query>>(
columns?: Query
): PostgrestTransformBuilder<Schema, Row, NewResult> {
): PostgrestTransformBuilder<Schema, Row, NewResult, ThrowOnError> {
// Remove whitespaces except when quoted
let quoted = false
const cleanedColumns = (columns ?? '*')
Expand All @@ -43,7 +44,7 @@ export default class PostgrestTransformBuilder<
this.headers['Prefer'] += ','
}
this.headers['Prefer'] += 'return=representation'
return this as unknown as PostgrestTransformBuilder<Schema, Row, NewResult>
return this as unknown as PostgrestTransformBuilder<Schema, Row, NewResult, ThrowOnError>
}

/**
Expand Down Expand Up @@ -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<PostgrestSingleResponse<Result>> {
single(): PromiseLike<PostgrestSingleResponse<Result, ThrowOnError>> {
this.headers['Accept'] = 'application/vnd.pgrst.object+json'
return this as PromiseLike<PostgrestSingleResponse<Result>>
return this as unknown as PromiseLike<PostgrestSingleResponse<Result, ThrowOnError>>
}

/**
Expand All @@ -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<PostgrestMaybeSingleResponse<Result>> {
maybeSingle(): PromiseLike<PostgrestMaybeSingleResponse<Result, ThrowOnError>> {
this.headers['Accept'] = 'application/vnd.pgrst.object+json'
this.allowEmpty = true
return this as PromiseLike<PostgrestMaybeSingleResponse<Result>>
return this as unknown as PromiseLike<PostgrestMaybeSingleResponse<Result, ThrowOnError>>
}

/**
* Return `data` as a string in CSV format.
*/
csv(): PromiseLike<PostgrestSingleResponse<string>> {
csv(): PromiseLike<PostgrestSingleResponse<string, ThrowOnError>> {
this.headers['Accept'] = 'text/csv'
return this as PromiseLike<PostgrestSingleResponse<string>>
return this as unknown as PromiseLike<PostgrestSingleResponse<string, ThrowOnError>>
}

/**
* Return `data` as an object in [GeoJSON](https://geojson.org) format.
*/
geojson(): PromiseLike<PostgrestSingleResponse<Record<string, unknown>>> {
geojson(): PromiseLike<PostgrestSingleResponse<Record<string, unknown>, ThrowOnError>> {
this.headers['Accept'] = 'application/geo+json'
return this as PromiseLike<PostgrestSingleResponse<Record<string, unknown>>>
return this as unknown as PromiseLike<
PostgrestSingleResponse<Record<string, unknown>, ThrowOnError>
>
}

/**
Expand Down Expand Up @@ -207,8 +210,8 @@ export default class PostgrestTransformBuilder<
wal?: boolean
format?: 'json' | 'text'
} = {}):
| PromiseLike<PostgrestResponse<Record<string, unknown>>>
| PromiseLike<PostgrestSingleResponse<string>> {
| PromiseLike<PostgrestResponse<Record<string, unknown>, ThrowOnError>>
| PromiseLike<PostgrestSingleResponse<string, ThrowOnError>> {
const options = [
analyze ? 'analyze' : null,
verbose ? 'verbose' : null,
Expand All @@ -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<PostgrestResponse<Record<string, unknown>>>
else return this as PromiseLike<PostgrestSingleResponse<string>>
if (format === 'json')
return this as PromiseLike<PostgrestResponse<Record<string, unknown>, ThrowOnError>>
else return this as unknown as PromiseLike<PostgrestSingleResponse<string, ThrowOnError>>
}

/**
Expand All @@ -246,7 +250,7 @@ export default class PostgrestTransformBuilder<
*
* @typeParam NewResult - The new result type to override with
*/
returns<NewResult>(): PostgrestTransformBuilder<Schema, Row, NewResult> {
return this as unknown as PostgrestTransformBuilder<Schema, Row, NewResult>
returns<NewResult>(): PostgrestTransformBuilder<Schema, Row, NewResult, ThrowOnError> {
return this as unknown as PostgrestTransformBuilder<Schema, Row, NewResult, ThrowOnError>
}
}
15 changes: 10 additions & 5 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,22 @@ interface PostgrestResponseFailure extends PostgrestResponseBase {
data: null
count: null
}
export type PostgrestResponse<T> = PostgrestResponseSuccess<T> | PostgrestResponseFailure
export type PostgrestResponse<T, ThrowOnError> = ThrowOnError extends true
? PostgrestResponseSuccess<T>
: PostgrestResponseSuccess<T> | PostgrestResponseFailure

interface PostgrestSingleResponseSuccess<T> extends PostgrestResponseBase {
error: null
data: T
count: number | null
}
export type PostgrestSingleResponse<T> =
| PostgrestSingleResponseSuccess<T>
| PostgrestResponseFailure
export type PostgrestMaybeSingleResponse<T> = PostgrestSingleResponse<T | null>
export type PostgrestSingleResponse<T, ThrowOnError> = ThrowOnError extends true
? PostgrestSingleResponseSuccess<T>
: PostgrestSingleResponseSuccess<T> | PostgrestResponseFailure
export type PostgrestMaybeSingleResponse<T, ThrowOnError> = PostgrestSingleResponse<
T | null,
ThrowOnError
>

export type GenericTable = {
Row: Record<string, unknown>
Expand Down