Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
38 changes: 34 additions & 4 deletions packages/core/postgrest-js/src/PostgrestTransformBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { GetResult } from './select-query-parser/result'
import { CheckMatchingArrayTypes } from './types/types'
import { ClientServerOptions, GenericSchema } from './types/common/common'
import type { MaxAffectedEnabled } from './types/feature-flags'
import type { TablesAndViews } from './select-query-parser/types'

export default class PostgrestTransformBuilder<
ClientOptions extends ClientServerOptions,
Expand Down Expand Up @@ -156,9 +157,23 @@ export default class PostgrestTransformBuilder<
column: ColumnName,
options?: { ascending?: boolean; nullsFirst?: boolean; referencedTable?: undefined }
): this
order(
order<ReferencedTable extends string & keyof TablesAndViews<Schema>, Column extends string>(
column: Column extends keyof TablesAndViews<Schema>[ReferencedTable]['Row']
? Column
: string extends Column
? string
: keyof TablesAndViews<Schema>[ReferencedTable]['Row'] & string,
options: { ascending?: boolean; nullsFirst?: boolean; referencedTable: ReferencedTable }
): this
order<ReferencedTable extends string>(
column: string,
options?: { ascending?: boolean; nullsFirst?: boolean; referencedTable?: string }
options?: {
ascending?: boolean
nullsFirst?: boolean
referencedTable?: ReferencedTable extends keyof TablesAndViews<Schema>
? never
: ReferencedTable
}
Comment on lines +187 to +195
): this
/**
* @deprecated Use `options.referencedTable` instead of `options.foreignTable`
Expand All @@ -170,9 +185,24 @@ export default class PostgrestTransformBuilder<
/**
* @deprecated Use `options.referencedTable` instead of `options.foreignTable`
*/
order(
order<ReferencedTable extends string & keyof TablesAndViews<Schema>, Column extends string>(
column: Column extends keyof TablesAndViews<Schema>[ReferencedTable]['Row']
? Column
: string extends Column
? string
: keyof TablesAndViews<Schema>[ReferencedTable]['Row'] & string,
options: { ascending?: boolean; nullsFirst?: boolean; foreignTable: ReferencedTable }
): this
/**
* @deprecated Use `options.referencedTable` instead of `options.foreignTable`
*/
order<ReferencedTable extends string>(
column: string,
options?: { ascending?: boolean; nullsFirst?: boolean; foreignTable?: string }
options?: {
ascending?: boolean
nullsFirst?: boolean
foreignTable?: ReferencedTable extends keyof TablesAndViews<Schema> ? never : ReferencedTable
}
Comment on lines +214 to +220
): this
/**
* Order the query result by `column`.
Expand Down
107 changes: 86 additions & 21 deletions packages/core/postgrest-js/test/index.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,28 @@ const REST_URL = 'http://localhost:54321/rest/v1'
const postgrest = new PostgrestClient<Database>(REST_URL)
const postgrestWithOptions = new PostgrestClient<DatabaseWithOptions>(REST_URL)

type WithThrowOnError<T> = T extends PostgrestFilterBuilder<
infer ClientOptions,
infer Schema,
infer Row,
infer Result,
infer RelationName,
infer Relationships,
infer Method,
boolean
>
? PostgrestFilterBuilder<
ClientOptions,
Schema,
Row,
Result,
RelationName,
Relationships,
Method,
true
>
: never
type WithThrowOnError<T> =
T extends PostgrestFilterBuilder<
infer ClientOptions,
infer Schema,
infer Row,
infer Result,
infer RelationName,
infer Relationships,
infer Method,
boolean
>
? PostgrestFilterBuilder<
ClientOptions,
Schema,
Row,
Result,
RelationName,
Relationships,
Method,
true
>
: never

// table and view name type safety
{
Expand Down Expand Up @@ -561,3 +562,67 @@ type WithThrowOnError<T> = T extends PostgrestFilterBuilder<
expectType<TypeEqual<typeof result.data, { id: number; message: string }[]>>(true)
}
}

// `.order()` with `referencedTable` provides typed column names from the referenced table (#971)
{
// Base case: order by parent table columns still works
postgrest.from('users').select('messages(*)').order('username')

// referencedTable provides autocomplete for the referenced table's columns
postgrest
.from('users')
.select('messages(*)')
.order('channel_id', { referencedTable: 'messages', ascending: false })

// Also works with other valid columns on the referenced table
postgrest
.from('users')
.select('messages(*)')
.order('message', { referencedTable: 'messages', ascending: false })

// Invalid column on a known referenced table should error
postgrest
.from('users')
.select('messages(*)')
// @ts-expect-error No overload matches this call.
.order('nonexistent_column', { referencedTable: 'messages', ascending: false })

// Deprecated foreignTable also provides typed column names
postgrest
.from('users')
.select('messages(*)')
.order('channel_id', { foreignTable: 'messages', ascending: false })

// Invalid column on a known referenced table should error (foreignTable)
postgrest
.from('users')
.select('messages(*)')
// @ts-expect-error No overload matches this call.
.order('nonexistent_column', { foreignTable: 'messages', ascending: false })

// Ordering by a column on 'channels' table works
postgrest
.from('messages')
.select('channels(*)')
.order('slug', { referencedTable: 'channels', ascending: true })

// Invalid column on channels should error
postgrest
.from('messages')
.select('channels(*)')
// @ts-expect-error No overload matches this call.
.order('bad_col', { referencedTable: 'channels', ascending: true })

// Dynamic (non-literal) column with a known referencedTable should compile
const sortColumn: string = 'channel_id'
postgrest
.from('users')
.select('messages(*)')
.order(sortColumn, { referencedTable: 'messages', ascending: false })

// Dynamic column with deprecated foreignTable should also compile
postgrest
.from('users')
.select('messages(*)')
.order(sortColumn, { foreignTable: 'messages', ascending: false })
}