Skip to content
This repository was archived by the owner on Oct 9, 2025. It is now read-only.

Commit 2bbc435

Browse files
authored
fix: rpc method (#156)
* refactor: move rpc method to PostgrestRpcBuilder * feat: support void rpc function * fix: rpc unit tests * chore: remove rpc from PostgrestQueryBuilder * fix: return PostgrestRpcBuilder * chore: add options param description * fix: Stored Procedure rpc not support HEAD request * fix: store procedure unit tests * chore: remove unused param * chore: create stored procedure returns void unit test * fix: fetch response and error parsing * chore: clean up * refactor: format code * chore: add comment * chore: revert try/catch error
1 parent 9ecb985 commit 2bbc435

File tree

7 files changed

+84
-76
lines changed

7 files changed

+84
-76
lines changed

src/PostgrestClient.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import PostgrestQueryBuilder from './lib/PostgrestQueryBuilder'
2+
import PostgrestRpcBuilder from './lib/PostgrestRpcBuilder'
23
import PostgrestTransformBuilder from './lib/PostgrestTransformBuilder'
34

45
export default class PostgrestClient {
@@ -47,11 +48,21 @@ export default class PostgrestClient {
4748
*
4849
* @param fn The function name to call.
4950
* @param params The parameters to pass to the function call.
51+
* @param count Count algorithm to use to count rows in a table.
5052
*/
51-
rpc<T = any>(fn: string, params?: object): PostgrestTransformBuilder<T> {
53+
rpc<T = any>(
54+
fn: string,
55+
params?: object,
56+
{
57+
count = null,
58+
}: {
59+
count?: null | 'exact' | 'planned' | 'estimated'
60+
} = {}
61+
): PostgrestTransformBuilder<T> {
5262
const url = `${this.url}/rpc/${fn}`
53-
return new PostgrestQueryBuilder<T>(url, { headers: this.headers, schema: this.schema }).rpc(
54-
params
55-
)
63+
return new PostgrestRpcBuilder<T>(url, {
64+
headers: this.headers,
65+
schema: this.schema,
66+
}).rpc(params, { count })
5667
}
5768
}

src/lib/PostgrestQueryBuilder.ts

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import { PostgrestBuilder } from './types'
22
import PostgrestFilterBuilder from './PostgrestFilterBuilder'
3-
import PostgrestTransformBuilder from './PostgrestTransformBuilder'
43

54
export default class PostgrestQueryBuilder<T> extends PostgrestBuilder<T> {
65
constructor(
@@ -142,26 +141,4 @@ export default class PostgrestQueryBuilder<T> extends PostgrestBuilder<T> {
142141
this.headers['Prefer'] = prefersHeaders.join(',')
143142
return new PostgrestFilterBuilder(this)
144143
}
145-
146-
/** @internal */
147-
rpc(
148-
params?: object,
149-
{
150-
head = false,
151-
count = null,
152-
}: {
153-
head?: boolean
154-
count?: null | 'exact' | 'planned' | 'estimated'
155-
} = {}
156-
): PostgrestTransformBuilder<T> {
157-
this.method = 'POST'
158-
this.body = params
159-
if (count) {
160-
this.headers['Prefer'] = `count=${count}`
161-
}
162-
if (head) {
163-
this.method = 'HEAD'
164-
}
165-
return new PostgrestTransformBuilder(this)
166-
}
167144
}

src/lib/PostgrestRpcBuilder.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { PostgrestBuilder } from './types'
2+
import PostgrestTransformBuilder from './PostgrestTransformBuilder'
3+
4+
export default class PostgrestRpcBuilder<T> extends PostgrestBuilder<T> {
5+
constructor(
6+
url: string,
7+
{ headers = {}, schema }: { headers?: { [key: string]: string }; schema?: string } = {}
8+
) {
9+
super({} as PostgrestBuilder<T>)
10+
this.url = new URL(url)
11+
this.headers = { ...headers }
12+
this.schema = schema
13+
}
14+
15+
/**
16+
* Perform a stored procedure call.
17+
*/
18+
rpc(
19+
params?: object,
20+
{
21+
count = null,
22+
}: {
23+
count?: null | 'exact' | 'planned' | 'estimated'
24+
} = {}
25+
): PostgrestTransformBuilder<T> {
26+
this.method = 'POST'
27+
this.body = params
28+
29+
if (count) {
30+
if (this.headers['Prefer'] !== undefined) this.headers['Prefer'] += `,count=${count}`
31+
else this.headers['Prefer'] = `count=${count}`
32+
}
33+
34+
return new PostgrestTransformBuilder(this)
35+
}
36+
}

src/lib/types.ts

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -83,32 +83,26 @@ export abstract class PostgrestBuilder<T> implements PromiseLike<PostgrestRespon
8383
body: JSON.stringify(this.body),
8484
})
8585
.then(async (res) => {
86-
let error, data, count
86+
let error = null
87+
let data = null
88+
let count = null
89+
8790
if (res.ok) {
88-
error = null
89-
if (this.method !== 'HEAD') {
90-
const isReturnMinimal = this.headers['Prefer']?.split(',').includes('return=minimal')
91-
data = isReturnMinimal ? null : await res.json()
92-
} else {
93-
data = null
91+
const isReturnMinimal = this.headers['Prefer']?.split(',').includes('return=minimal')
92+
if (this.method !== 'HEAD' && !isReturnMinimal) {
93+
const text = await res.text()
94+
if (text && text !== '') data = JSON.parse(text)
9495
}
9596

9697
const countHeader = this.headers['Prefer']?.match(/count=(exact|planned|estimated)/)
97-
if (countHeader) {
98-
const contentRange = res.headers.get('content-range')?.split('/')
99-
if (contentRange && contentRange.length > 1) {
100-
count = parseInt(contentRange[1])
101-
} else {
102-
count = null
103-
}
104-
} else {
105-
count = null
98+
const contentRange = res.headers.get('content-range')?.split('/')
99+
if (countHeader && contentRange && contentRange.length > 1) {
100+
count = parseInt(contentRange[1])
106101
}
107102
} else {
108103
error = await res.json()
109-
data = null
110-
count = null
111104
}
105+
112106
const postgrestResponse: PostgrestResponse<T> = {
113107
error,
114108
data,

test/__snapshots__/index.test.ts.snap

Lines changed: 11 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2883,35 +2883,25 @@ Object {
28832883
}
28842884
`;
28852885
2886-
exports[`stored procedure with count: 'exact' 1`] = `
2886+
exports[`stored procedure returns void 1`] = `
28872887
Object {
28882888
"body": null,
28892889
"count": null,
28902890
"data": null,
2891-
"error": Object {
2892-
"code": "42883",
2893-
"details": null,
2894-
"hint": "No function matches the given name and argument types. You might need to add explicit type casts.",
2895-
"message": "function public.get_status(count => text, name_param => text) does not exist",
2896-
},
2897-
"status": 404,
2898-
"statusText": "Not Found",
2891+
"error": null,
2892+
"status": 200,
2893+
"statusText": "OK",
28992894
}
29002895
`;
29012896
2902-
exports[`stored procedure with count: 'exact', head: true 1`] = `
2897+
exports[`stored procedure with count: 'exact' 1`] = `
29032898
Object {
2904-
"body": null,
2905-
"count": null,
2906-
"data": null,
2907-
"error": Object {
2908-
"code": "42883",
2909-
"details": null,
2910-
"hint": "No function matches the given name and argument types. You might need to add explicit type casts.",
2911-
"message": "function public.get_status(count => text, head => text, name_param => text) does not exist",
2912-
},
2913-
"status": 404,
2914-
"statusText": "Not Found",
2899+
"body": "ONLINE",
2900+
"count": 1,
2901+
"data": "ONLINE",
2902+
"error": null,
2903+
"status": 200,
2904+
"statusText": "OK",
29152905
}
29162906
`;
29172907

test/basic.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ test('stored procedure', async () => {
1313
expect(res).toMatchSnapshot()
1414
})
1515

16+
test('stored procedure returns void', async () => {
17+
const res = await postgrest.rpc('void_func')
18+
expect(res).toMatchSnapshot()
19+
})
20+
1621
test('custom headers', async () => {
1722
const postgrest = new PostgrestClient(REST_URL, { headers: { apikey: 'foo' } })
1823
expect((postgrest.from('users').select() as any).headers['apikey']).toEqual('foo')
@@ -171,16 +176,7 @@ test('select with count:exact', async () => {
171176
})
172177

173178
test("stored procedure with count: 'exact'", async () => {
174-
const res = await postgrest.rpc('get_status', { name_param: 'supabot', count: 'exact' })
175-
expect(res).toMatchSnapshot()
176-
})
177-
178-
test("stored procedure with count: 'exact', head: true", async () => {
179-
const res = await postgrest.rpc('get_status', {
180-
name_param: 'supabot',
181-
count: 'exact',
182-
head: true,
183-
})
179+
const res = await postgrest.rpc('get_status', { name_param: 'supabot'}, {count: 'exact' })
184180
expect(res).toMatchSnapshot()
185181
})
186182

test/db/00-schema.sql

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ RETURNS TABLE(username text, status user_status) AS $$
4747
SELECT username, status from users WHERE username=name_param;
4848
$$ LANGUAGE SQL IMMUTABLE;
4949

50+
CREATE FUNCTION public.void_func()
51+
RETURNS void AS $$
52+
$$ LANGUAGE SQL;
53+
5054
-- SECOND SCHEMA USERS
5155
CREATE TYPE personal.user_status AS ENUM ('ONLINE', 'OFFLINE');
5256
CREATE TABLE personal.users(

0 commit comments

Comments
 (0)