Skip to content
This repository was archived by the owner on Oct 9, 2025. It is now read-only.
Merged
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
72 changes: 62 additions & 10 deletions src/lib/PostgrestQueryBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,28 @@ export default class PostgrestQueryBuilder<T> extends PostgrestBuilder<T> {
* Performs an INSERT into the table.
*
* @param values The values to insert.
* @param upsert If `true`, performs an UPSERT.
* @param onConflict By specifying the `on_conflict` query parameter, you can make UPSERT work on a column(s) that has a UNIQUE constraint.
* @param returning By default the new record is returned. Set this to 'minimal' if you don't need this value.
* @param count Count algorithm to use to count rows in a table.
*/
insert(
values: Partial<T> | Partial<T>[],
options?: {
returning?: 'minimal' | 'representation'
count?: null | 'exact' | 'planned' | 'estimated'
}
): PostgrestFilterBuilder<T>
/**
* @deprecated Use `upsert()` instead.
*/
insert(
values: Partial<T> | Partial<T>[],
options?: {
upsert?: boolean
onConflict?: string
returning?: 'minimal' | 'representation'
count?: null | 'exact' | 'planned' | 'estimated'
}
): PostgrestFilterBuilder<T>
insert(
values: Partial<T> | Partial<T>[],
{
Expand All @@ -78,18 +96,52 @@ export default class PostgrestQueryBuilder<T> extends PostgrestBuilder<T> {
): PostgrestFilterBuilder<T> {
this.method = 'POST'

let prefersHeaders = []
prefersHeaders.push(`return=${returning}`)
const prefersHeaders = [`return=${returning}`]
if (upsert) prefersHeaders.push('resolution=merge-duplicates')

if (upsert && onConflict !== undefined) this.url.searchParams.set('on_conflict', onConflict)
this.body = values
if (count) {
prefersHeaders.push(`count=${count}`)
}

this.headers['Prefer'] = prefersHeaders.join(',')


return new PostgrestFilterBuilder(this)
}

/**
* Performs an UPSERT into the table.
*
* @param values The values to insert.
* @param onConflict By specifying the `on_conflict` query parameter, you can make UPSERT work on a column(s) that has a UNIQUE constraint.
* @param returning By default the new record is returned. Set this to 'minimal' if you don't need this value.
* @param count Count algorithm to use to count rows in a table.
*/
upsert(
values: Partial<T> | Partial<T>[],
{
onConflict,
returning = 'representation',
count = null,
}: {
onConflict?: string
returning?: 'minimal' | 'representation'
count?: null | 'exact' | 'planned' | 'estimated'
} = {}
): PostgrestFilterBuilder<T> {
this.method = 'POST'

const prefersHeaders = ['resolution=merge-duplicates', `return=${returning}`]

if (onConflict !== undefined) this.url.searchParams.set('on_conflict', onConflict)
this.body = values
if (count) {
prefersHeaders.push(`count=${count}`)
}

this.headers['Prefer'] = prefersHeaders.join(',')

return new PostgrestFilterBuilder(this)
}

Expand All @@ -98,6 +150,7 @@ export default class PostgrestQueryBuilder<T> extends PostgrestBuilder<T> {
*
* @param values The values to update.
* @param returning By default the updated record is returned. Set this to 'minimal' if you don't need this value.
* @param count Count algorithm to use to count rows in a table.
*/
update(
values: Partial<T>,
Expand All @@ -110,8 +163,7 @@ export default class PostgrestQueryBuilder<T> extends PostgrestBuilder<T> {
} = {}
): PostgrestFilterBuilder<T> {
this.method = 'PATCH'
let prefersHeaders = []
prefersHeaders.push(`return=${returning}`)
const prefersHeaders = [`return=${returning}`]
this.body = values
if (count) {
prefersHeaders.push(`count=${count}`)
Expand All @@ -124,6 +176,7 @@ export default class PostgrestQueryBuilder<T> extends PostgrestBuilder<T> {
* Performs a DELETE on the table.
*
* @param returning If `true`, return the deleted row(s) in the response.
* @param count Count algorithm to use to count rows in a table.
*/
delete({
returning = 'representation',
Expand All @@ -133,8 +186,7 @@ export default class PostgrestQueryBuilder<T> extends PostgrestBuilder<T> {
count?: null | 'exact' | 'planned' | 'estimated'
} = {}): PostgrestFilterBuilder<T> {
this.method = 'DELETE'
let prefersHeaders = []
prefersHeaders.push(`return=${returning}`)
const prefersHeaders = [`return=${returning}`]
if (count) {
prefersHeaders.push(`count=${count}`)
}
Expand Down
11 changes: 4 additions & 7 deletions test/basic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ test('switch schema', async () => {
test('on_conflict insert', async () => {
const res = await postgrest
.from('users')
.insert({ username: 'dragarcia' }, { upsert: true, onConflict: 'username' })
.upsert({ username: 'dragarcia' }, { onConflict: 'username' })
expect(res).toMatchSnapshot()
})

Expand All @@ -55,7 +55,7 @@ describe('basic insert, update, delete', () => {
test('upsert', async () => {
let res = await postgrest
.from('messages')
.insert({ id: 3, message: 'foo', username: 'supabot', channel_id: 2 }, { upsert: true })
.upsert({ id: 3, message: 'foo', username: 'supabot', channel_id: 2 })
expect(res).toMatchSnapshot()

res = await postgrest.from('messages').select()
Expand Down Expand Up @@ -176,7 +176,7 @@ test('select with count:exact', async () => {
})

test("stored procedure with count: 'exact'", async () => {
const res = await postgrest.rpc('get_status', { name_param: 'supabot'}, {count: 'exact' })
const res = await postgrest.rpc('get_status', { name_param: 'supabot' }, { count: 'exact' })
expect(res).toMatchSnapshot()
})

Expand All @@ -194,10 +194,7 @@ describe("insert, update, delete with count: 'exact'", () => {
test("upsert with count: 'exact'", async () => {
let res = await postgrest
.from('messages')
.insert(
{ id: 3, message: 'foo', username: 'supabot', channel_id: 2 },
{ upsert: true, count: 'exact' }
)
.upsert({ id: 3, message: 'foo', username: 'supabot', channel_id: 2 }, { count: 'exact' })
expect(res).toMatchSnapshot()

res = await postgrest.from('messages').select()
Expand Down
10 changes: 6 additions & 4 deletions test/resource-embedding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,9 @@ describe('embedded filters', () => {
const res = await postgrest
.from('users')
.select('messages(*)')
.or('channel_id.eq.2,and(message.eq.Hello World 👋,username.eq.supabot)', { foreignTable: 'messages' })
.or('channel_id.eq.2,and(message.eq.Hello World 👋,username.eq.supabot)', {
foreignTable: 'messages',
})
expect(res).toMatchSnapshot()
})
})
Expand All @@ -38,14 +40,14 @@ describe('embedded transforms', () => {
expect(res).toMatchSnapshot()
})

test('embedded order on multiple columns', async () => {
test('embedded order on multiple columns', async () => {
const res = await postgrest
.from('users')
.select('messages(*)')
.order('channel_id', { foreignTable: 'messages', ascending: false })
.order('username', { foreignTable: 'messages', ascending: false })
expect(res).toMatchSnapshot()
})
expect(res).toMatchSnapshot()
})

test('embedded limit', async () => {
const res = await postgrest
Expand Down