Skip to content

Commit

Permalink
feat: Prefer: return=minimal by default
Browse files Browse the repository at this point in the history
BREAKING CHANGE: set `Prefer: return=minimal` by default everywhere

We previously set `returning = 'representation'` by default so that
inserted/updated/deleted rows are returned. We now change the default so that
inserted/updated/deleted rows are NOT returned.

To return inserted/updated/deleted rows, call `.select()` at the end of
the call chain.
  • Loading branch information
soedirgo committed Aug 5, 2022
1 parent d8facba commit dfb808a
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 43 deletions.
28 changes: 7 additions & 21 deletions src/PostgrestQueryBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,22 +74,19 @@ export default class PostgrestQueryBuilder<T> extends PostgrestBuilder<T> {
* Performs an INSERT into the table.
*
* @param values The values to insert.
* @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>[],
{
returning = 'representation',
count = null,
}: {
returning?: 'minimal' | 'representation'
count?: null | 'exact' | 'planned' | 'estimated'
} = {}
): PostgrestFilterBuilder<T> {
this.method = 'POST'

const prefersHeaders = [`return=${returning}`]
const prefersHeaders = []
this.body = values
if (count) {
prefersHeaders.push(`count=${count}`)
Expand All @@ -114,31 +111,26 @@ export default class PostgrestQueryBuilder<T> extends PostgrestBuilder<T> {
* 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.
* @param ignoreDuplicates Specifies if duplicate rows should be ignored and not inserted.
* @param options Named parameters.
* @param options.onConflict By specifying the `on_conflict` query parameter, you can make UPSERT work on a column(s) that has a UNIQUE constraint.
* @param options.ignoreDuplicates Specifies if duplicate rows should be ignored and not inserted.
*/
upsert(
values: Partial<T> | Partial<T>[],
{
onConflict,
returning = 'representation',
count = null,
ignoreDuplicates = false,
}: {
onConflict?: string
returning?: 'minimal' | 'representation'
count?: null | 'exact' | 'planned' | 'estimated'
ignoreDuplicates?: boolean
} = {}
): PostgrestFilterBuilder<T> {
this.method = 'POST'

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

if (onConflict !== undefined) this.url.searchParams.set('on_conflict', onConflict)
this.body = values
Expand All @@ -157,21 +149,18 @@ export default class PostgrestQueryBuilder<T> extends PostgrestBuilder<T> {
* Performs an UPDATE on the table.
*
* @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>,
{
returning = 'representation',
count = null,
}: {
returning?: 'minimal' | 'representation'
count?: null | 'exact' | 'planned' | 'estimated'
} = {}
): PostgrestFilterBuilder<T> {
this.method = 'PATCH'
const prefersHeaders = [`return=${returning}`]
const prefersHeaders = []
this.body = values
if (count) {
prefersHeaders.push(`count=${count}`)
Expand All @@ -186,18 +175,15 @@ 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',
count = null,
}: {
returning?: 'minimal' | 'representation'
count?: null | 'exact' | 'planned' | 'estimated'
} = {}): PostgrestFilterBuilder<T> {
this.method = 'DELETE'
const prefersHeaders = [`return=${returning}`]
const prefersHeaders = []
if (count) {
prefersHeaders.push(`count=${count}`)
}
Expand Down
4 changes: 4 additions & 0 deletions src/PostgrestTransformBuilder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ export default class PostgrestTransformBuilder<T> extends PostgrestBuilder<T> {
})
.join('')
this.url.searchParams.set('select', cleanedColumns)
if (this.headers['Prefer']) {
this.headers['Prefer'] += ','
}
this.headers['Prefer'] += 'return=representation'
return this
}

Expand Down
70 changes: 49 additions & 21 deletions test/basic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,34 @@ test('custom headers', async () => {
describe('custom prefer headers with ', () => {
test('insert', async () => {
const postgrest = new PostgrestClient(REST_URL, { headers: { Prefer: 'tx=rollback' } })
const postgrestFilterBuilder = postgrest.from('users').insert({ username: 'dragarcia' }) as any
const postgrestFilterBuilder = postgrest
.from('users')
.insert({ username: 'dragarcia' })
.select() as any
expect(postgrestFilterBuilder.headers['Prefer']).toContain('tx=rollback')
expect(postgrestFilterBuilder.headers['Prefer']).toContain('return=')
})
test('update', async () => {
const postgrest = new PostgrestClient(REST_URL, { headers: { Prefer: 'tx=rollback' } })
const postgrestFilterBuilder = postgrest.from('users').update({ username: 'dragarcia' }) as any
const postgrestFilterBuilder = postgrest
.from('users')
.update({ username: 'dragarcia' })
.select() as any
expect(postgrestFilterBuilder.headers['Prefer']).toContain('tx=rollback')
expect(postgrestFilterBuilder.headers['Prefer']).toContain('return=')
})
test('upsert', async () => {
const postgrest = new PostgrestClient(REST_URL, { headers: { Prefer: 'tx=rollback' } })
const postgrestFilterBuilder = postgrest.from('users').upsert({ username: 'dragarcia' }) as any
const postgrestFilterBuilder = postgrest
.from('users')
.upsert({ username: 'dragarcia' })
.select() as any
expect(postgrestFilterBuilder.headers['Prefer']).toContain('tx=rollback')
expect(postgrestFilterBuilder.headers['Prefer']).toContain('return=')
})
test('delete', async () => {
const postgrest = new PostgrestClient(REST_URL, { headers: { Prefer: 'tx=rollback' } })
const postgrestFilterBuilder = postgrest.from('users').delete() as any
const postgrestFilterBuilder = postgrest.from('users').delete().select() as any
expect(postgrestFilterBuilder.headers['Prefer']).toContain('tx=rollback')
expect(postgrestFilterBuilder.headers['Prefer']).toContain('return=')
})
Expand All @@ -65,13 +74,15 @@ test('on_conflict insert', async () => {
const res = await postgrest
.from('users')
.upsert({ username: 'dragarcia' }, { onConflict: 'username' })
.select()
expect(res).toMatchSnapshot()
})

test('ignoreDuplicates upsert', async () => {
const res = await postgrest
.from('users')
.upsert({ username: 'dragarcia' }, { onConflict: 'username', ignoreDuplicates: true })
.select()
expect(res).toMatchSnapshot()
})

Expand All @@ -80,6 +91,7 @@ describe('basic insert, update, delete', () => {
let res = await postgrest
.from('messages')
.insert({ message: 'foo', username: 'supabot', channel_id: 1 })
.select()
expect(res).toMatchSnapshot()

res = await postgrest.from('messages').select()
Expand All @@ -90,33 +102,41 @@ describe('basic insert, update, delete', () => {
let res = await postgrest
.from('messages')
.upsert({ id: 3, message: 'foo', username: 'supabot', channel_id: 2 })
.select()
expect(res).toMatchSnapshot()

res = await postgrest.from('messages').select()
expect(res).toMatchSnapshot()
})

test('bulk insert', async () => {
let res = await postgrest.from('messages').insert([
{ message: 'foo', username: 'supabot', channel_id: 1 },
{ message: 'foo', username: 'supabot', channel_id: 1 },
])
let res = await postgrest
.from('messages')
.insert([
{ message: 'foo', username: 'supabot', channel_id: 1 },
{ message: 'foo', username: 'supabot', channel_id: 1 },
])
.select()
expect(res).toMatchSnapshot()

res = await postgrest.from('messages').select()
expect(res).toMatchSnapshot()
})

test('basic update', async () => {
let res = await postgrest.from('messages').update({ channel_id: 2 }).eq('message', 'foo')
let res = await postgrest
.from('messages')
.update({ channel_id: 2 })
.eq('message', 'foo')
.select()
expect(res).toMatchSnapshot()

res = await postgrest.from('messages').select()
expect(res).toMatchSnapshot()
})

test('basic delete', async () => {
let res = await postgrest.from('messages').delete().eq('message', 'foo')
let res = await postgrest.from('messages').delete().eq('message', 'foo').select()
expect(res).toMatchSnapshot()

res = await postgrest.from('messages').select()
Expand Down Expand Up @@ -249,9 +269,7 @@ test('allow ordering on JSON column', async () => {
})

test('Prefer: return=minimal', async () => {
const { data } = await postgrest
.from('users')
.insert({ username: 'bar' }, { returning: 'minimal' })
const { data } = await postgrest.from('users').insert({ username: 'bar' })
expect(data).toMatchSnapshot()

await postgrest.from('users').delete().eq('username', 'bar')
Expand Down Expand Up @@ -305,6 +323,7 @@ describe("insert, update, delete with count: 'exact'", () => {
let res = await postgrest
.from('messages')
.insert({ message: 'foo', username: 'supabot', channel_id: 1 }, { count: 'exact' })
.select()
expect(res).toMatchSnapshot()

res = await postgrest.from('messages').select()
Expand All @@ -315,20 +334,24 @@ describe("insert, update, delete with count: 'exact'", () => {
let res = await postgrest
.from('messages')
.upsert({ id: 3, message: 'foo', username: 'supabot', channel_id: 2 }, { count: 'exact' })
.select()
expect(res).toMatchSnapshot()

res = await postgrest.from('messages').select()
expect(res).toMatchSnapshot()
})

test("bulk insert with count: 'exact'", async () => {
let res = await postgrest.from('messages').insert(
[
{ message: 'foo', username: 'supabot', channel_id: 1 },
{ message: 'foo', username: 'supabot', channel_id: 1 },
],
{ count: 'exact' }
)
let res = await postgrest
.from('messages')
.insert(
[
{ message: 'foo', username: 'supabot', channel_id: 1 },
{ message: 'foo', username: 'supabot', channel_id: 1 },
],
{ count: 'exact' }
)
.select()
expect(res).toMatchSnapshot()

res = await postgrest.from('messages').select()
Expand All @@ -340,14 +363,19 @@ describe("insert, update, delete with count: 'exact'", () => {
.from('messages')
.update({ channel_id: 2 }, { count: 'exact' })
.eq('message', 'foo')
.select()
expect(res).toMatchSnapshot()

res = await postgrest.from('messages').select()
expect(res).toMatchSnapshot()
})

test("basic delete count: 'exact'", async () => {
let res = await postgrest.from('messages').delete({ count: 'exact' }).eq('message', 'foo')
let res = await postgrest
.from('messages')
.delete({ count: 'exact' })
.eq('message', 'foo')
.select()
expect(res).toMatchSnapshot()

res = await postgrest.from('messages').select()
Expand Down
2 changes: 1 addition & 1 deletion test/transforms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ test('single', async () => {
})

test('single on insert', async () => {
const res = await postgrest.from('users').insert({ username: 'foo' }).single()
const res = await postgrest.from('users').insert({ username: 'foo' }).select().single()
expect(res).toMatchSnapshot()

await postgrest.from('users').delete().eq('username', 'foo')
Expand Down

0 comments on commit dfb808a

Please sign in to comment.