From c4f1d653866eaddf1a8ec0a2197512a427341ef6 Mon Sep 17 00:00:00 2001 From: Misha Kaletsky Date: Tue, 31 Oct 2023 23:03:40 -0400 Subject: [PATCH 01/13] non-nullable throwOnError --- src/PostgrestBuilder.ts | 24 +++++++++++------------- test/index.test-d.ts | 11 +++++++++++ 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/src/PostgrestBuilder.ts b/src/PostgrestBuilder.ts index 23e397f1..3b52a6ab 100644 --- a/src/PostgrestBuilder.ts +++ b/src/PostgrestBuilder.ts @@ -1,9 +1,9 @@ // @ts-ignore import nodeFetch from '@supabase/node-fetch' -import type { Fetch, PostgrestSingleResponse } from './types' +import type { Fetch, PostgrestResponseSuccess, PostgrestSingleResponse } from './types' -export default abstract class PostgrestBuilder +export default abstract class PostgrestBuilder implements PromiseLike> { protected method: 'GET' | 'HEAD' | 'POST' | 'PATCH' | 'DELETE' @@ -11,12 +11,12 @@ export default abstract class PostgrestBuilder protected headers: Record protected schema?: string protected body?: unknown - protected shouldThrowOnError = false + protected shouldThrowOnError: boolean protected signal?: AbortSignal protected fetch: Fetch protected isMaybeSingle: boolean - constructor(builder: PostgrestBuilder) { + constructor(builder: PostgrestBuilder) { this.method = builder.method this.url = builder.url this.headers = builder.headers @@ -35,20 +35,18 @@ export default abstract class PostgrestBuilder } } - /** - * If there's an error with the query, throwOnError will reject the promise by - * throwing the error instead of returning it as part of a successful response. - * - * {@link https://github.com/supabase/supabase-js/issues/92} - */ - throwOnError(): this { + throwOnError(): PostgrestBuilder { this.shouldThrowOnError = true - return this + return this as PostgrestBuilder } then, TResult2 = never>( onfulfilled?: - | ((value: PostgrestSingleResponse) => TResult1 | PromiseLike) + | (( + value: ThrowOnError extends true + ? PostgrestResponseSuccess + : PostgrestSingleResponse + ) => TResult1 | PromiseLike) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null diff --git a/test/index.test-d.ts b/test/index.test-d.ts index 23ea6b72..3acc5cb4 100644 --- a/test/index.test-d.ts +++ b/test/index.test-d.ts @@ -97,3 +97,14 @@ const postgrest = new PostgrestClient(REST_URL) const res = await postgrest.from('users').select('username, dat') expectType[]>>(res) } + +// throw on error +{ + const { data } = await postgrest + .from('users') + .select('*') + .returns<{ a: string; b: number }[]>() + .throwOnError() + + expectType<{ a: string; b: number }>(data[0]) +} From 8632e842e3e5a37fe821b21d95dfed25386ceab7 Mon Sep 17 00:00:00 2001 From: Misha Kaletsky Date: Wed, 1 Nov 2023 23:55:01 -0400 Subject: [PATCH 02/13] mostly parity --- src/PostgrestBuilder.ts | 2 +- src/PostgrestClient.ts | 41 +++++++++++++++------ src/PostgrestFilterBuilder.ts | 5 +-- src/PostgrestQueryBuilder.ts | 38 ++++++++++++-------- src/PostgrestTransformBuilder.ts | 61 ++++++++++++++++++++++---------- test/resource-embedding.ts | 3 +- 6 files changed, 103 insertions(+), 47 deletions(-) diff --git a/src/PostgrestBuilder.ts b/src/PostgrestBuilder.ts index 3b52a6ab..d1da46f5 100644 --- a/src/PostgrestBuilder.ts +++ b/src/PostgrestBuilder.ts @@ -3,7 +3,7 @@ import nodeFetch from '@supabase/node-fetch' import type { Fetch, PostgrestResponseSuccess, PostgrestSingleResponse } from './types' -export default abstract class PostgrestBuilder +export default abstract class PostgrestBuilder implements PromiseLike> { protected method: 'GET' | 'HEAD' | 'POST' | 'PATCH' | 'DELETE' diff --git a/src/PostgrestClient.ts b/src/PostgrestClient.ts index e7d9678a..2d67da62 100644 --- a/src/PostgrestClient.ts +++ b/src/PostgrestClient.ts @@ -21,12 +21,14 @@ export default class PostgrestClient< : string & keyof Database, Schema extends GenericSchema = Database[SchemaName] extends GenericSchema ? Database[SchemaName] - : any + : any, + ThrowOnError extends boolean = false > { url: string headers: Record schemaName?: SchemaName fetch?: Fetch + shouldThrowOnError: ThrowOnError // TODO: Add back shouldThrowOnError once we figure out the typings /** @@ -44,37 +46,50 @@ export default class PostgrestClient< headers = {}, schema, fetch, + shouldThrowOnError, }: { headers?: Record schema?: SchemaName fetch?: Fetch + shouldThrowOnError?: ThrowOnError } = {} ) { this.url = url this.headers = { ...DEFAULT_HEADERS, ...headers } this.schemaName = schema this.fetch = fetch + this.shouldThrowOnError = Boolean(shouldThrowOnError) as ThrowOnError + } + + throwOnError(): PostgrestClient { + return new PostgrestClient(this.url, { + headers: this.headers, + schema: this.schemaName, + fetch: this.fetch, + shouldThrowOnError: true, + }) as PostgrestClient } from< TableName extends string & keyof Schema['Tables'], Table extends Schema['Tables'][TableName] - >(relation: TableName): PostgrestQueryBuilder + >(relation: TableName): PostgrestQueryBuilder from( relation: ViewName - ): PostgrestQueryBuilder - from(relation: string): PostgrestQueryBuilder + ): PostgrestQueryBuilder + from(relation: string): PostgrestQueryBuilder /** * Perform a query on a table or a view. * * @param relation - The table or view name to query */ - 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.schemaName, fetch: this.fetch, + shouldThrowOnError: this.shouldThrowOnError, }) } @@ -90,16 +105,19 @@ export default class PostgrestClient< ): PostgrestClient< Database, DynamicSchema, - Database[DynamicSchema] extends GenericSchema ? Database[DynamicSchema] : any + Database[DynamicSchema] extends GenericSchema ? Database[DynamicSchema] : any, + ThrowOnError > { return new PostgrestClient< Database, DynamicSchema, - Database[DynamicSchema] extends GenericSchema ? Database[DynamicSchema] : any + Database[DynamicSchema] extends GenericSchema ? Database[DynamicSchema] : any, + ThrowOnError >(this.url, { headers: this.headers, schema, fetch: this.fetch, + shouldThrowOnError: this.shouldThrowOnError, }) } @@ -144,7 +162,9 @@ export default class PostgrestClient< ? Function_['Returns'][number] : never : never, - Function_['Returns'] + Function_['Returns'], + unknown, + ThrowOnError > { let method: 'HEAD' | 'POST' const url = new URL(`${this.url}/rpc/${fn}`) @@ -172,6 +192,7 @@ export default class PostgrestClient< body, fetch: this.fetch, allowEmpty: false, - } as unknown as PostgrestBuilder) + shouldThrowOnError: this.shouldThrowOnError, + } as unknown as PostgrestBuilder) } } diff --git a/src/PostgrestFilterBuilder.ts b/src/PostgrestFilterBuilder.ts index e8da9a98..35d00307 100644 --- a/src/PostgrestFilterBuilder.ts +++ b/src/PostgrestFilterBuilder.ts @@ -29,8 +29,9 @@ export default class PostgrestFilterBuilder< Schema extends GenericSchema, Row extends Record, Result, - Relationships = unknown -> extends PostgrestTransformBuilder { + Relationships, // = unknown, + ThrowOnError extends boolean // = false +> extends PostgrestTransformBuilder { eq( column: ColumnName, value: NonNullable diff --git a/src/PostgrestQueryBuilder.ts b/src/PostgrestQueryBuilder.ts index 26776e87..ec5e2312 100644 --- a/src/PostgrestQueryBuilder.ts +++ b/src/PostgrestQueryBuilder.ts @@ -6,6 +6,7 @@ import { Fetch, GenericSchema, GenericTable, GenericView } from './types' export default class PostgrestQueryBuilder< Schema extends GenericSchema, Relation extends GenericTable | GenericView, + ThrowOnError extends boolean, Relationships = Relation extends { Relationships: infer R } ? R : unknown > { url: URL @@ -13,6 +14,7 @@ export default class PostgrestQueryBuilder< schema?: string signal?: AbortSignal fetch?: Fetch + shouldThrowOnError: ThrowOnError constructor( url: URL, @@ -20,16 +22,19 @@ export default class PostgrestQueryBuilder< headers = {}, schema, fetch, + shouldThrowOnError, }: { headers?: Record schema?: string fetch?: Fetch + shouldThrowOnError?: ThrowOnError } ) { this.url = url this.headers = headers this.schema = schema this.fetch = fetch + this.shouldThrowOnError = Boolean(shouldThrowOnError) as ThrowOnError } /** @@ -65,7 +70,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 @@ -93,7 +98,8 @@ export default class PostgrestQueryBuilder< schema: this.schema, fetch: this.fetch, allowEmpty: false, - } as unknown as PostgrestBuilder) + shouldThrowOnError: this.shouldThrowOnError, + } as unknown as PostgrestBuilder) } // TODO(v3): Make `defaultToNull` consistent for both single & bulk inserts. @@ -102,14 +108,14 @@ export default class PostgrestQueryBuilder< options?: { count?: 'exact' | 'planned' | 'estimated' } - ): PostgrestFilterBuilder + ): PostgrestFilterBuilder insert( values: Row[], options?: { count?: 'exact' | 'planned' | 'estimated' defaultToNull?: boolean } - ): PostgrestFilterBuilder + ): PostgrestFilterBuilder /** * Perform an INSERT into the table or view. * @@ -145,7 +151,7 @@ export default class PostgrestQueryBuilder< count?: 'exact' | 'planned' | 'estimated' defaultToNull?: boolean } = {} - ): PostgrestFilterBuilder { + ): PostgrestFilterBuilder { const method = 'POST' const prefersHeaders = [] @@ -176,7 +182,8 @@ export default class PostgrestQueryBuilder< body: values, fetch: this.fetch, allowEmpty: false, - } as unknown as PostgrestBuilder) + shouldThrowOnError: this.shouldThrowOnError, + } as unknown as PostgrestBuilder) } // TODO(v3): Make `defaultToNull` consistent for both single & bulk upserts. @@ -187,7 +194,7 @@ export default class PostgrestQueryBuilder< ignoreDuplicates?: boolean count?: 'exact' | 'planned' | 'estimated' } - ): PostgrestFilterBuilder + ): PostgrestFilterBuilder upsert( values: Row[], options?: { @@ -196,7 +203,7 @@ export default class PostgrestQueryBuilder< count?: 'exact' | 'planned' | 'estimated' defaultToNull?: boolean } - ): PostgrestFilterBuilder + ): PostgrestFilterBuilder /** * Perform an UPSERT on the table or view. Depending on the column(s) passed * to `onConflict`, `.upsert()` allows you to perform the equivalent of @@ -248,7 +255,7 @@ export default class PostgrestQueryBuilder< count?: 'exact' | 'planned' | 'estimated' defaultToNull?: boolean } = {} - ): PostgrestFilterBuilder { + ): PostgrestFilterBuilder { const method = 'POST' const prefersHeaders = [`resolution=${ignoreDuplicates ? 'ignore' : 'merge'}-duplicates`] @@ -281,7 +288,8 @@ export default class PostgrestQueryBuilder< body: values, fetch: this.fetch, allowEmpty: false, - } as unknown as PostgrestBuilder) + shouldThrowOnError: this.shouldThrowOnError, + } as unknown as PostgrestBuilder) } /** @@ -312,7 +320,7 @@ export default class PostgrestQueryBuilder< }: { count?: 'exact' | 'planned' | 'estimated' } = {} - ): PostgrestFilterBuilder { + ): PostgrestFilterBuilder { const method = 'PATCH' const prefersHeaders = [] if (this.headers['Prefer']) { @@ -331,7 +339,8 @@ export default class PostgrestQueryBuilder< body: values, fetch: this.fetch, allowEmpty: false, - } as unknown as PostgrestBuilder) + shouldThrowOnError: this.shouldThrowOnError, + } as unknown as PostgrestBuilder) } /** @@ -357,7 +366,7 @@ export default class PostgrestQueryBuilder< count, }: { count?: 'exact' | 'planned' | 'estimated' - } = {}): PostgrestFilterBuilder { + } = {}): PostgrestFilterBuilder { const method = 'DELETE' const prefersHeaders = [] if (count) { @@ -375,6 +384,7 @@ export default class PostgrestQueryBuilder< schema: this.schema, fetch: this.fetch, allowEmpty: false, - } as unknown as PostgrestBuilder) + shouldThrowOnError: this.shouldThrowOnError, + } as unknown as PostgrestBuilder) } } diff --git a/src/PostgrestTransformBuilder.ts b/src/PostgrestTransformBuilder.ts index 8cbf87a1..fedb36c7 100644 --- a/src/PostgrestTransformBuilder.ts +++ b/src/PostgrestTransformBuilder.ts @@ -6,8 +6,9 @@ export default class PostgrestTransformBuilder< Schema extends GenericSchema, Row extends Record, Result, - Relationships = unknown -> extends PostgrestBuilder { + Relationships, + ThrowOnError extends boolean +> extends PostgrestBuilder { /** * Perform a SELECT on the query result. * @@ -19,7 +20,7 @@ export default class PostgrestTransformBuilder< */ select>( columns?: Query - ): PostgrestTransformBuilder { + ): PostgrestTransformBuilder { // Remove whitespaces except when quoted let quoted = false const cleanedColumns = (columns ?? '*') @@ -39,7 +40,13 @@ export default class PostgrestTransformBuilder< this.headers['Prefer'] += ',' } this.headers['Prefer'] += 'return=representation' - return this as unknown as PostgrestTransformBuilder + return this as unknown as PostgrestTransformBuilder< + Schema, + Row, + NewResultOne[], + Relationships, + ThrowOnError + > } order( @@ -138,11 +145,12 @@ export default class PostgrestTransformBuilder< * Query result must be one row (e.g. using `.limit(1)`), otherwise this * returns an error. */ - single< - ResultOne = Result extends (infer ResultOne)[] ? ResultOne : never - >(): PostgrestBuilder { + single(): PostgrestBuilder< + ResultOne, + ThrowOnError + > { this.headers['Accept'] = 'application/vnd.pgrst.object+json' - return this as PostgrestBuilder + return this as unknown as PostgrestBuilder } /** @@ -153,7 +161,7 @@ export default class PostgrestTransformBuilder< */ maybeSingle< ResultOne = Result extends (infer ResultOne)[] ? ResultOne : never - >(): PostgrestBuilder { + >(): PostgrestBuilder { // Temporary partial fix for https://github.com/supabase/postgrest-js/issues/361 // Issue persists e.g. for `.insert([...]).select().maybeSingle()` if (this.method === 'GET') { @@ -162,23 +170,23 @@ export default class PostgrestTransformBuilder< this.headers['Accept'] = 'application/vnd.pgrst.object+json' } this.isMaybeSingle = true - return this as PostgrestBuilder + return this as unknown as PostgrestBuilder } /** * Return `data` as a string in CSV format. */ - csv(): PostgrestBuilder { + csv(): PostgrestBuilder { this.headers['Accept'] = 'text/csv' - return this as PostgrestBuilder + return this as unknown as PostgrestBuilder } /** * Return `data` as an object in [GeoJSON](https://geojson.org) format. */ - geojson(): PostgrestBuilder> { + geojson(): PostgrestBuilder, ThrowOnError> { this.headers['Accept'] = 'application/geo+json' - return this as PostgrestBuilder> + return this as unknown as PostgrestBuilder, ThrowOnError> } /** @@ -216,7 +224,9 @@ export default class PostgrestTransformBuilder< buffers?: boolean wal?: boolean format?: 'json' | 'text' - } = {}): PostgrestBuilder[]> | PostgrestBuilder { + } = {}): + | PostgrestBuilder[], ThrowOnError> + | PostgrestBuilder { const options = [ analyze ? 'analyze' : null, verbose ? 'verbose' : null, @@ -231,8 +241,9 @@ export default class PostgrestTransformBuilder< this.headers[ 'Accept' ] = `application/vnd.pgrst.plan+${format}; for="${forMediatype}"; options=${options};` - if (format === 'json') return this as PostgrestBuilder[]> - else return this as PostgrestBuilder + if (format === 'json') + return this as unknown as PostgrestBuilder[], ThrowOnError> + else return this as unknown as PostgrestBuilder } /** @@ -254,7 +265,19 @@ export default class PostgrestTransformBuilder< * * @typeParam NewResult - The new result type to override with */ - returns(): PostgrestTransformBuilder { - return this as unknown as PostgrestTransformBuilder + returns(): PostgrestTransformBuilder< + Schema, + Row, + NewResult, + Relationships, + ThrowOnError + > { + return this as unknown as PostgrestTransformBuilder< + Schema, + Row, + NewResult, + Relationships, + ThrowOnError + > } } diff --git a/test/resource-embedding.ts b/test/resource-embedding.ts index 08887be0..b94806a0 100644 --- a/test/resource-embedding.ts +++ b/test/resource-embedding.ts @@ -1,10 +1,11 @@ import { PostgrestClient } from '../src/index' import { Database } from './types' -const postgrest = new PostgrestClient('http://localhost:3000') +const postgrest = new PostgrestClient('http://localhost:3000').throwOnError() test('embedded select', async () => { const res = await postgrest.from('users').select('messages(*)') + res.data.slice() expect(res).toMatchInlineSnapshot(` Object { "count": null, From 2cf1b64db79752d1e52567759f008681bf0c40d4 Mon Sep 17 00:00:00 2001 From: Misha Kaletsky Date: Thu, 2 Nov 2023 00:16:22 -0400 Subject: [PATCH 03/13] update tests --- test/basic.ts | 2 +- test/index.test-d.ts | 23 ++++++++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/test/basic.ts b/test/basic.ts index 0c948828..1cd2e275 100644 --- a/test/basic.ts +++ b/test/basic.ts @@ -716,8 +716,8 @@ test('maybeSingle w/ throwOnError', async () => { .from('messages') .select() .eq('message', 'i do not exist') - .throwOnError() .maybeSingle() + .throwOnError() .then(undefined, () => { passes = false }) diff --git a/test/index.test-d.ts b/test/index.test-d.ts index 3acc5cb4..c769e07c 100644 --- a/test/index.test-d.ts +++ b/test/index.test-d.ts @@ -98,13 +98,22 @@ const postgrest = new PostgrestClient(REST_URL) expectType[]>>(res) } -// throw on error +// throw on error on query { - const { data } = await postgrest - .from('users') - .select('*') - .returns<{ a: string; b: number }[]>() - .throwOnError() + const { data } = await postgrest.from('users').select('username').throwOnError() + expectType<{ username: string }[]>(data) +} + +// queries without throw on error have nullable results +{ + const { data } = await postgrest.from('users').select('username') + expectType<{ username: string }[] | null>(data) +} + +// throw on error on client +{ + const strictClient = postgrest.throwOnError() - expectType<{ a: string; b: number }>(data[0]) + const { data } = await strictClient.from('users').select('username') + expectType<{ username: string }[]>(data) } From a3223ad2eede56df7490f1e63eb31b50a0dd3c56 Mon Sep 17 00:00:00 2001 From: Misha Kaletsky Date: Thu, 2 Nov 2023 00:22:09 -0400 Subject: [PATCH 04/13] maybeSingle test --- test/index.test-d.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/index.test-d.ts b/test/index.test-d.ts index c769e07c..08ea54a0 100644 --- a/test/index.test-d.ts +++ b/test/index.test-d.ts @@ -104,6 +104,12 @@ const postgrest = new PostgrestClient(REST_URL) expectType<{ username: string }[]>(data) } +// throw on error with maybeSingle is still nullable +{ + const { data } = await postgrest.from('users').select('username').maybeSingle().throwOnError() + expectType<{ username: string } | null>(data) +} + // queries without throw on error have nullable results { const { data } = await postgrest.from('users').select('username') From 204cdf10d6b6dcee7d84273d5db89c4068da73a5 Mon Sep 17 00:00:00 2001 From: Misha Kaletsky Date: Thu, 2 Nov 2023 00:41:14 -0400 Subject: [PATCH 05/13] revert --- test/resource-embedding.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/resource-embedding.ts b/test/resource-embedding.ts index b94806a0..08887be0 100644 --- a/test/resource-embedding.ts +++ b/test/resource-embedding.ts @@ -1,11 +1,10 @@ import { PostgrestClient } from '../src/index' import { Database } from './types' -const postgrest = new PostgrestClient('http://localhost:3000').throwOnError() +const postgrest = new PostgrestClient('http://localhost:3000') test('embedded select', async () => { const res = await postgrest.from('users').select('messages(*)') - res.data.slice() expect(res).toMatchInlineSnapshot(` Object { "count": null, From 737f5c8bcbfdc06b4f1d6209cd3ff08e0a133f04 Mon Sep 17 00:00:00 2001 From: Misha Kaletsky Date: Wed, 8 Nov 2023 21:07:53 -0500 Subject: [PATCH 06/13] rm commented out code --- src/PostgrestFilterBuilder.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PostgrestFilterBuilder.ts b/src/PostgrestFilterBuilder.ts index 35d00307..44e5a76e 100644 --- a/src/PostgrestFilterBuilder.ts +++ b/src/PostgrestFilterBuilder.ts @@ -29,8 +29,8 @@ export default class PostgrestFilterBuilder< Schema extends GenericSchema, Row extends Record, Result, - Relationships, // = unknown, - ThrowOnError extends boolean // = false + Relationships, + ThrowOnError extends boolean > extends PostgrestTransformBuilder { eq( column: ColumnName, From 452e0b06a99494becaf30073b01c52db16dfcccb Mon Sep 17 00:00:00 2001 From: Misha Kaletsky Date: Wed, 8 Nov 2023 23:09:15 -0500 Subject: [PATCH 07/13] backwards compatible typeargs --- src/PostgrestBuilder.ts | 2 +- src/PostgrestClient.ts | 10 +++++----- src/PostgrestFilterBuilder.ts | 4 ++-- src/PostgrestQueryBuilder.ts | 4 ++-- src/PostgrestTransformBuilder.ts | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/PostgrestBuilder.ts b/src/PostgrestBuilder.ts index d1da46f5..3b52a6ab 100644 --- a/src/PostgrestBuilder.ts +++ b/src/PostgrestBuilder.ts @@ -3,7 +3,7 @@ import nodeFetch from '@supabase/node-fetch' import type { Fetch, PostgrestResponseSuccess, PostgrestSingleResponse } from './types' -export default abstract class PostgrestBuilder +export default abstract class PostgrestBuilder implements PromiseLike> { protected method: 'GET' | 'HEAD' | 'POST' | 'PATCH' | 'DELETE' diff --git a/src/PostgrestClient.ts b/src/PostgrestClient.ts index 2d67da62..f5bce19f 100644 --- a/src/PostgrestClient.ts +++ b/src/PostgrestClient.ts @@ -73,19 +73,19 @@ export default class PostgrestClient< from< TableName extends string & keyof Schema['Tables'], Table extends Schema['Tables'][TableName] - >(relation: TableName): PostgrestQueryBuilder + >(relation: TableName): PostgrestQueryBuilder from( relation: ViewName - ): PostgrestQueryBuilder - from(relation: string): PostgrestQueryBuilder + ): PostgrestQueryBuilder + from(relation: string): PostgrestQueryBuilder /** * Perform a query on a table or a view. * * @param relation - The table or view name to query */ - 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.schemaName, fetch: this.fetch, diff --git a/src/PostgrestFilterBuilder.ts b/src/PostgrestFilterBuilder.ts index 44e5a76e..bd5b60f4 100644 --- a/src/PostgrestFilterBuilder.ts +++ b/src/PostgrestFilterBuilder.ts @@ -29,8 +29,8 @@ export default class PostgrestFilterBuilder< Schema extends GenericSchema, Row extends Record, Result, - Relationships, - ThrowOnError extends boolean + Relationships = unknown, + ThrowOnError extends boolean = false > extends PostgrestTransformBuilder { eq( column: ColumnName, diff --git a/src/PostgrestQueryBuilder.ts b/src/PostgrestQueryBuilder.ts index ec5e2312..2593ccb4 100644 --- a/src/PostgrestQueryBuilder.ts +++ b/src/PostgrestQueryBuilder.ts @@ -6,8 +6,8 @@ import { Fetch, GenericSchema, GenericTable, GenericView } from './types' export default class PostgrestQueryBuilder< Schema extends GenericSchema, Relation extends GenericTable | GenericView, - ThrowOnError extends boolean, - Relationships = Relation extends { Relationships: infer R } ? R : unknown + Relationships = Relation extends { Relationships: infer R } ? R : unknown, + ThrowOnError extends boolean = false > { url: URL headers: Record diff --git a/src/PostgrestTransformBuilder.ts b/src/PostgrestTransformBuilder.ts index fedb36c7..5873be0f 100644 --- a/src/PostgrestTransformBuilder.ts +++ b/src/PostgrestTransformBuilder.ts @@ -6,8 +6,8 @@ export default class PostgrestTransformBuilder< Schema extends GenericSchema, Row extends Record, Result, - Relationships, - ThrowOnError extends boolean + Relationships = unknown, + ThrowOnError extends boolean = false > extends PostgrestBuilder { /** * Perform a SELECT on the query result. From a3f795860e3bfc9c8ff68dc2906c66f55d81c249 Mon Sep 17 00:00:00 2001 From: Misha Kaletsky Date: Wed, 8 Nov 2023 23:13:14 -0500 Subject: [PATCH 08/13] prettier --- src/PostgrestClient.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/PostgrestClient.ts b/src/PostgrestClient.ts index f5bce19f..53cffdaf 100644 --- a/src/PostgrestClient.ts +++ b/src/PostgrestClient.ts @@ -73,10 +73,22 @@ export default class PostgrestClient< from< TableName extends string & keyof Schema['Tables'], Table extends Schema['Tables'][TableName] - >(relation: TableName): PostgrestQueryBuilder + >( + relation: TableName + ): PostgrestQueryBuilder< + Schema, + Table, + Table extends { Relationships: infer R } ? R : unknown, + ThrowOnError + > from( relation: ViewName - ): PostgrestQueryBuilder + ): PostgrestQueryBuilder< + Schema, + View, + View extends { Relationships: infer R } ? R : unknown, + ThrowOnError + > from(relation: string): PostgrestQueryBuilder /** * Perform a query on a table or a view. From 455e5bb7136e50accd2a810d9bc4b36ec255b9ed Mon Sep 17 00:00:00 2001 From: Misha Kaletsky Date: Tue, 4 Jun 2024 11:43:00 -0400 Subject: [PATCH 09/13] format + test + types --- src/PostgrestClient.ts | 12 ++++- src/PostgrestFilterBuilder.ts | 9 +++- src/PostgrestQueryBuilder.ts | 81 ++++++++++++++++++++++++++++---- src/PostgrestTransformBuilder.ts | 9 +++- test/index.test-d.ts | 9 ++++ 5 files changed, 108 insertions(+), 12 deletions(-) diff --git a/src/PostgrestClient.ts b/src/PostgrestClient.ts index 9ec6a82b..3516c997 100644 --- a/src/PostgrestClient.ts +++ b/src/PostgrestClient.ts @@ -214,6 +214,16 @@ export default class PostgrestClient< fetch: this.fetch, allowEmpty: false, shouldThrowOnError: this.shouldThrowOnError, - } as unknown as PostgrestBuilder) + } as unknown as PostgrestBuilder) as PostgrestFilterBuilder< + Schema, + Fn['Returns'] extends any[] + ? Fn['Returns'][number] extends Record + ? Fn['Returns'][number] + : never + : never, + Fn['Returns'], + unknown, + ThrowOnError + > } } diff --git a/src/PostgrestFilterBuilder.ts b/src/PostgrestFilterBuilder.ts index 4d10c9f1..daf3399e 100644 --- a/src/PostgrestFilterBuilder.ts +++ b/src/PostgrestFilterBuilder.ts @@ -32,7 +32,14 @@ export default class PostgrestFilterBuilder< RelationName = unknown, Relationships = unknown, ThrowOnError extends boolean = false -> extends PostgrestTransformBuilder { +> extends PostgrestTransformBuilder< + Schema, + Row, + Result, + RelationName, + Relationships, + ThrowOnError +> { eq( column: ColumnName, value: NonNullable diff --git a/src/PostgrestQueryBuilder.ts b/src/PostgrestQueryBuilder.ts index b0c46c51..a5ae966b 100644 --- a/src/PostgrestQueryBuilder.ts +++ b/src/PostgrestQueryBuilder.ts @@ -71,7 +71,14 @@ export default class PostgrestQueryBuilder< head?: boolean count?: 'exact' | 'planned' | 'estimated' } = {} - ): PostgrestFilterBuilder { + ): PostgrestFilterBuilder< + Schema, + Relation['Row'], + ResultOne[], + RelationName, + Relationships, + ThrowOnError + > { const method = head ? 'HEAD' : 'GET' // Remove whitespaces except when quoted let quoted = false @@ -109,14 +116,28 @@ export default class PostgrestQueryBuilder< options?: { count?: 'exact' | 'planned' | 'estimated' } - ): PostgrestFilterBuilder + ): PostgrestFilterBuilder< + Schema, + Relation['Row'], + null, + RelationName, + Relationships, + ThrowOnError + > insert( values: Row[], options?: { count?: 'exact' | 'planned' | 'estimated' defaultToNull?: boolean } - ): PostgrestFilterBuilder + ): PostgrestFilterBuilder< + Schema, + Relation['Row'], + null, + RelationName, + Relationships, + ThrowOnError + > /** * Perform an INSERT into the table or view. * @@ -152,7 +173,14 @@ export default class PostgrestQueryBuilder< count?: 'exact' | 'planned' | 'estimated' defaultToNull?: boolean } = {} - ): PostgrestFilterBuilder { + ): PostgrestFilterBuilder< + Schema, + Relation['Row'], + null, + RelationName, + Relationships, + ThrowOnError + > { const method = 'POST' const prefersHeaders = [] @@ -195,7 +223,14 @@ export default class PostgrestQueryBuilder< ignoreDuplicates?: boolean count?: 'exact' | 'planned' | 'estimated' } - ): PostgrestFilterBuilder + ): PostgrestFilterBuilder< + Schema, + Relation['Row'], + null, + RelationName, + Relationships, + ThrowOnError + > upsert( values: Row[], options?: { @@ -204,7 +239,14 @@ export default class PostgrestQueryBuilder< count?: 'exact' | 'planned' | 'estimated' defaultToNull?: boolean } - ): PostgrestFilterBuilder + ): PostgrestFilterBuilder< + Schema, + Relation['Row'], + null, + RelationName, + Relationships, + ThrowOnError + > /** * Perform an UPSERT on the table or view. Depending on the column(s) passed * to `onConflict`, `.upsert()` allows you to perform the equivalent of @@ -256,7 +298,14 @@ export default class PostgrestQueryBuilder< count?: 'exact' | 'planned' | 'estimated' defaultToNull?: boolean } = {} - ): PostgrestFilterBuilder { + ): PostgrestFilterBuilder< + Schema, + Relation['Row'], + null, + RelationName, + Relationships, + ThrowOnError + > { const method = 'POST' const prefersHeaders = [`resolution=${ignoreDuplicates ? 'ignore' : 'merge'}-duplicates`] @@ -321,7 +370,14 @@ export default class PostgrestQueryBuilder< }: { count?: 'exact' | 'planned' | 'estimated' } = {} - ): PostgrestFilterBuilder { + ): PostgrestFilterBuilder< + Schema, + Relation['Row'], + null, + RelationName, + Relationships, + ThrowOnError + > { const method = 'PATCH' const prefersHeaders = [] if (this.headers['Prefer']) { @@ -367,7 +423,14 @@ export default class PostgrestQueryBuilder< count, }: { count?: 'exact' | 'planned' | 'estimated' - } = {}): PostgrestFilterBuilder { + } = {}): PostgrestFilterBuilder< + Schema, + Relation['Row'], + null, + RelationName, + Relationships, + ThrowOnError + > { const method = 'DELETE' const prefersHeaders = [] if (count) { diff --git a/src/PostgrestTransformBuilder.ts b/src/PostgrestTransformBuilder.ts index 0bf38fe9..f0e7deba 100644 --- a/src/PostgrestTransformBuilder.ts +++ b/src/PostgrestTransformBuilder.ts @@ -24,7 +24,14 @@ export default class PostgrestTransformBuilder< NewResultOne = GetResult >( columns?: Query - ): PostgrestTransformBuilder { + ): PostgrestTransformBuilder< + Schema, + Row, + NewResultOne[], + RelationName, + Relationships, + ThrowOnError + > { // Remove whitespaces except when quoted let quoted = false const cleanedColumns = (columns ?? '*') diff --git a/test/index.test-d.ts b/test/index.test-d.ts index f3f9cd9c..ad2ef178 100644 --- a/test/index.test-d.ts +++ b/test/index.test-d.ts @@ -195,6 +195,15 @@ const postgrest = new PostgrestClient(REST_URL) expectType<{ username: string } | null>(data) } +// throw on error with rpc +{ + const { data: nullable } = await postgrest.rpc('get_status', { name_param: 'foo' }) + expectType<'ONLINE' | 'OFFLINE' | null>(nullable) + + const { data: notnull } = await postgrest.rpc('get_status', { name_param: 'foo' }).throwOnError() + expectType<'ONLINE' | 'OFFLINE'>(notnull) +} + // queries without throw on error have nullable results { const { data } = await postgrest.from('users').select('username') From f913a39e8f74669123010194eb9559c376163c47 Mon Sep 17 00:00:00 2001 From: Misha Kaletsky Date: Tue, 4 Jun 2024 12:03:33 -0400 Subject: [PATCH 10/13] as never --- src/PostgrestClient.ts | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/src/PostgrestClient.ts b/src/PostgrestClient.ts index 3516c997..7a0fd337 100644 --- a/src/PostgrestClient.ts +++ b/src/PostgrestClient.ts @@ -122,12 +122,7 @@ export default class PostgrestClient< Database[DynamicSchema] extends GenericSchema ? Database[DynamicSchema] : any, ThrowOnError > { - return new PostgrestClient< - Database, - DynamicSchema, - Database[DynamicSchema] extends GenericSchema ? Database[DynamicSchema] : any, - ThrowOnError - >(this.url, { + return new PostgrestClient(this.url, { headers: this.headers, schema, fetch: this.fetch, @@ -214,16 +209,6 @@ export default class PostgrestClient< fetch: this.fetch, allowEmpty: false, shouldThrowOnError: this.shouldThrowOnError, - } as unknown as PostgrestBuilder) as PostgrestFilterBuilder< - Schema, - Fn['Returns'] extends any[] - ? Fn['Returns'][number] extends Record - ? Fn['Returns'][number] - : never - : never, - Fn['Returns'], - unknown, - ThrowOnError - > + } as never) } } From e5cff13be6d72552bf8eb672e284c4feb9f953f8 Mon Sep 17 00:00:00 2001 From: Misha Kaletsky Date: Tue, 18 Jun 2024 13:19:54 -0400 Subject: [PATCH 11/13] rm superfluous overload --- src/PostgrestClient.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/PostgrestClient.ts b/src/PostgrestClient.ts index 7a0fd337..a2196185 100644 --- a/src/PostgrestClient.ts +++ b/src/PostgrestClient.ts @@ -91,7 +91,6 @@ export default class PostgrestClient< View extends { Relationships: infer R } ? R : unknown, ThrowOnError > - from(relation: string): PostgrestQueryBuilder /** * Perform a query on a table or a view. * From 4921d96716a87eb47791d2d306b93057f4ec35d0 Mon Sep 17 00:00:00 2001 From: Misha Kaletsky Date: Wed, 14 Aug 2024 17:22:12 -0400 Subject: [PATCH 12/13] pkg.pr.new --- .github/workflows/pkg.pr.new.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/pkg.pr.new.yml diff --git a/.github/workflows/pkg.pr.new.yml b/.github/workflows/pkg.pr.new.yml new file mode 100644 index 00000000..fb83646f --- /dev/null +++ b/.github/workflows/pkg.pr.new.yml @@ -0,0 +1,21 @@ +name: pkg.pr.new + +on: + push: {} + +jobs: + run: + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v2 + - name: Set up Node + uses: actions/setup-node@v2 + with: + node-version: '16' + + - run: | + npm clean-install + npm run build + + - name: Create pkg.pr.new release + run: npx pkg-pr-new publish From 0fc18e0e75eb584e18e914a8f1e0f7fa6956e5e4 Mon Sep 17 00:00:00 2001 From: Misha Kaletsky Date: Thu, 15 Aug 2024 14:12:18 -0400 Subject: [PATCH 13/13] avoid node 16 --- .github/workflows/pkg.pr.new.yml | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/.github/workflows/pkg.pr.new.yml b/.github/workflows/pkg.pr.new.yml index fb83646f..092d0f95 100644 --- a/.github/workflows/pkg.pr.new.yml +++ b/.github/workflows/pkg.pr.new.yml @@ -7,15 +7,8 @@ jobs: run: runs-on: ubuntu-20.04 steps: - - uses: actions/checkout@v2 - - name: Set up Node - uses: actions/setup-node@v2 - with: - node-version: '16' - - - run: | - npm clean-install - npm run build - + - uses: actions/checkout@v4 + - run: npm clean-install + - run: npm run build - name: Create pkg.pr.new release run: npx pkg-pr-new publish