1 | import { createClient, SupabaseClient } from "@supabase/[email protected]" |
2 |
|
3 | |
4 | * @param token Supabase `access_token` and `refresh_token`. `expires_at` (optional) is a UNIX |
5 | * timestamp in seconds. |
6 | * |
7 | * @param count Count algorithm to use to count rows in the table or view. |
8 | * `"exact"`: Exact but slow count algorithm. Performs a `COUNT(*)` under the hood. |
9 | * `"planned"`: Approximated but fast count algorithm. Uses the Postgres statistics under the hood. |
10 | * `"estimated"`: Uses exact count for low numbers and planned count for high numbers. |
11 | * |
12 | * @param head When set to `true`, `data` will not be returned. |
13 | * Useful if you only need the count. |
14 | * |
15 | * @param filter Learn more at https://supabase.com/docs/reference/javascript/filter |
16 | * |
17 | * @param order Learn more at https://supabase.com/docs/reference/javascript/order |
18 | * |
19 | * @param limit Learn more at https://supabase.com/docs/reference/javascript/limit |
20 | */ |
21 | type Supabase = { |
22 | url: string; |
23 | key: string; |
24 | }; |
25 | export async function main( |
26 | auth: Supabase, |
27 | table: string, |
28 | columns?: string, |
29 | token?: { |
30 | access: string; |
31 | refresh: string; |
32 | expires_at?: number; |
33 | }, |
34 | count?: "exact" | "planned" | "estimated", |
35 | head?: boolean, |
36 | filter?: { |
37 | column: string; |
38 | operator: string; |
39 | value: any; |
40 | }, |
41 | order?: { |
42 | column: string; |
43 | foreignTable: string; |
44 | ascending?: boolean; |
45 | nullsFirst?: boolean; |
46 | }, |
47 | limit?: { |
48 | count: number; |
49 | foreignTable?: string; |
50 | }, |
51 | ) { |
52 | return await refreshAndRetryIfExpired(auth, token, async (client) => { |
53 | const options = head || count ? { head, count } : undefined; |
54 | let query = client.from(table).select(columns || undefined, options); |
55 | if (filter?.column) { |
56 | query = query.filter(filter.column, filter.operator, filter.value); |
57 | } |
58 | if (order?.column) { |
59 | const { column, ...options } = order; |
60 | query = query.order(column, options); |
61 | } |
62 | if (limit?.count) { |
63 | const { count, foreignTable } = limit; |
64 | query = query.limit(count, foreignTable ? { foreignTable } : undefined); |
65 | } |
66 |
|
67 | return query; |
68 | }); |
69 | } |
70 |
|
71 | async function refreshAndRetryIfExpired( |
72 | auth: { url: string; key: string }, |
73 | token: { access: string; refresh: string; expires_at?: number } | undefined, |
74 | fn: (client: SupabaseClient) => Promise<{ data: any; error?: any }>, |
75 | ): Promise<{ data: any; error?: any; token?: { access: string; refresh: string; expires_at?: number } }> { |
76 | const makeClient = async (autoRefreshToken: boolean) => { |
77 | const client = createClient(auth.url, auth.key, { |
78 | auth: { autoRefreshToken, persistSession: false }, |
79 | }) |
80 | if (token) { |
81 | await client.auth.setSession({ access_token: token.access, refresh_token: token.refresh }) |
82 | } |
83 | return client |
84 | } |
85 | try { |
86 | let result = await fn(await makeClient(false)) |
87 | if (result?.error?.code === "PGRST301" && token) { |
88 | const client = await makeClient(true) |
89 | result = await fn(client) |
90 | const { data } = await client.auth.getSession() |
91 | if (data?.session) { |
92 | token = { |
93 | access: data.session.access_token, |
94 | refresh: data.session.refresh_token, |
95 | expires_at: data.session.expires_at, |
96 | } |
97 | } |
98 | } |
99 | return { ...result, token } |
100 | } catch (error) { |
101 | return { data: null, error } |
102 | } |
103 | } |
104 |
|