0

Upsert data

by
Published Jan 23, 2023
Script supabase Verified

The script

Submitted by hugo989 Bun
Verified 6 days ago
1
import { createClient, SupabaseClient } from "@supabase/[email protected]"
2

3
/**
4
 * @param on_conflict Comma-separated UNIQUE column(s) to specify how duplicate
5
 * rows are determined. Two rows are duplicates if all the onConflict columns are equal.
6
 *
7
 * @param ignore_duplicates If true, duplicate rows are ignored. If false, duplicate
8
 * rows are merged with existing rows.
9
 *
10
 * @param token Supabase `access_token` and `refresh_token`. `expires_at` (optional) is a UNIX
11
 * timestamp in seconds.
12
 *
13
 * @param count Count algorithm to use to count rows in the table or view.
14
 * `"exact"`: Exact but slow count algorithm. Performs a `COUNT(*)` under the hood.
15
 * `"planned"`: Approximated but fast count algorithm. Uses the Postgres statistics under the hood.
16
 * `"estimated"`: Uses exact count for low numbers and planned count for high numbers.
17
 */
18
type Supabase = {
19
  url: string;
20
  key: string;
21
};
22
export async function main(
23
  auth: Supabase,
24
  table: string,
25
  values: any,
26
  on_conflict?: string,
27
  ignore_duplicates: boolean = true,
28
  return_updated: boolean = false,
29
  token?: {
30
    access: string;
31
    refresh: string;
32
    expires_at?: number;
33
  },
34
  count?: "exact" | "planned" | "estimated",
35
) {
36
  return await refreshAndRetryIfExpired(auth, token, async (client) => {
37
    let query: any = client
38
      .from(table)
39
      .upsert(
40
        values,
41
        removeObjectEmptyFields({
42
          onConflict: on_conflict,
43
          ignoreDuplicates: ignore_duplicates,
44
          count,
45
        }),
46
      );
47
    if (return_updated) {
48
      query = query.select();
49
    }
50

51
    return query;
52
  });
53
}
54

55
function removeObjectEmptyFields(
56
  object?: Record<string, any>,
57
  removeEmptyArraysAndObjects = true,
58
  createNewObject = true,
59
) {
60
  if (!object || typeof object !== "object") return {}
61
  const obj = createNewObject ? { ...object } : object
62
  const emptyValues = [undefined, null, ""]
63
  for (const key in obj) {
64
    const value = obj[key]
65
    if (emptyValues.includes(value)) {
66
      delete obj[key]
67
    } else if (typeof value === "object") {
68
      if (Object.keys(value).length) {
69
        obj[key] = removeObjectEmptyFields(value, removeEmptyArraysAndObjects, false)
70
      }
71
      if (!Object.keys(value).length && removeEmptyArraysAndObjects) {
72
        delete obj[key]
73
      }
74
    }
75
  }
76
  return obj
77
}
78

79
async function refreshAndRetryIfExpired(
80
  auth: { url: string; key: string },
81
  token: { access: string; refresh: string; expires_at?: number } | undefined,
82
  fn: (client: SupabaseClient) => Promise<{ data: any; error?: any }>,
83
): Promise<{ data: any; error?: any; token?: { access: string; refresh: string; expires_at?: number } }> {
84
  const makeClient = async (autoRefreshToken: boolean) => {
85
    const client = createClient(auth.url, auth.key, {
86
      auth: { autoRefreshToken, persistSession: false },
87
    })
88
    if (token) {
89
      await client.auth.setSession({ access_token: token.access, refresh_token: token.refresh })
90
    }
91
    return client
92
  }
93
  try {
94
    let result = await fn(await makeClient(false))
95
    if (result?.error?.code === "PGRST301" && token) {
96
      const client = await makeClient(true)
97
      result = await fn(client)
98
      const { data } = await client.auth.getSession()
99
      if (data?.session) {
100
        token = {
101
          access: data.session.access_token,
102
          refresh: data.session.refresh_token,
103
          expires_at: data.session.expires_at,
104
        }
105
      }
106
    }
107
    return { ...result, token }
108
  } catch (error) {
109
    return { data: null, error }
110
  }
111
}
112

Other submissions
  • Submitted by adam186 Deno
    Created 398 days ago
    1
    import {
    2
      refreshAndRetryIfExpired,
    3
      removeObjectEmptyFields,
    4
    } from "https://deno.land/x/[email protected]/mod.ts";
    5
    
    
    6
    /**
    7
     * @param on_conflict Comma-separated UNIQUE column(s) to specify how duplicate
    8
     * rows are determined. Two rows are duplicates if all the onConflict columns are equal.
    9
     *
    10
     * @param ignore_duplicates If true, duplicate rows are ignored. If false, duplicate
    11
     * rows are merged with existing rows.
    12
     *
    13
     * @param token Supabase `access_token` and `refresh_token`. `expires_at` (optional) is a UNIX
    14
     * timestamp in seconds.
    15
     *
    16
     * @param count Count algorithm to use to count rows in the table or view.
    17
     * `"exact"`: Exact but slow count algorithm. Performs a `COUNT(*)` under the hood.
    18
     * `"planned"`: Approximated but fast count algorithm. Uses the Postgres statistics under the hood.
    19
     * `"estimated"`: Uses exact count for low numbers and planned count for high numbers.
    20
     */
    21
    type Supabase = {
    22
      url: string;
    23
      key: string;
    24
    };
    25
    export async function main(
    26
      auth: Supabase,
    27
      table: string,
    28
      values: any,
    29
      on_conflict?: string,
    30
      ignore_duplicates: boolean = true,
    31
      return_updated: boolean = false,
    32
      token?: {
    33
        access: string;
    34
        refresh: string;
    35
        expires_at?: number;
    36
      },
    37
      count?: "exact" | "planned" | "estimated",
    38
    ) {
    39
      return await refreshAndRetryIfExpired(auth, token, async (client) => {
    40
        let query: any = client
    41
          .from(table)
    42
          .upsert(
    43
            values,
    44
            removeObjectEmptyFields({ on_conflict, ignore_duplicates, count }),
    45
          );
    46
        if (return_updated) {
    47
          query = query.select();
    48
        }
    49
    
    
    50
        return query;
    51
      });
    52
    }
    53