Skip to content

Instantly share code, notes, and snippets.

@MartinMalinda
Created May 29, 2023 16:44
Show Gist options
  • Save MartinMalinda/c77e1691f3e8839955bbe2f0653492d3 to your computer and use it in GitHub Desktop.
Save MartinMalinda/c77e1691f3e8839955bbe2f0653492d3 to your computer and use it in GitHub Desktop.
import { SupabaseClient } from "@supabase/supabase-js";
import { GenericSchema } from "@supabase/supabase-js/dist/module/lib/types";
import { supabase } from "/~/data/supabase";
import { defineStore } from "pinia";
export function defineSupaStore<Database, SchemaName extends string & keyof Database, Schema extends GenericSchema, TableName extends string & keyof Schema['Tables']>(supabase: SupabaseClient<Database, SchemaName, Schema>, tableName: TableName) {
const table = () => supabase.from(tableName);
type Row = Schema['Tables'][TableName]['Row'];
type Insert = Schema['Tables'][TableName]['Insert'];
type Update = Schema['Tables'][TableName]['Update'];
type FilterBuilder = ReturnType<ReturnType<typeof table>['select']>;
return defineStore(`supabase-${String(tableName)}`, {
state: () => {
const state = {
[tableName]: {}
} as { [K in TableName]: Record<string, Row> };
return state;
},
actions: {
add(entities: Row[]) {
return entities.map(entity => (this as any)[tableName][(entity as any).id] = entity) as Row[];
},
async select(key: '*' | undefined) {
const { data, error } = await table().select(key);
if (error) {
throw error;
}
return this.add(data);
},
async update(id: Row['id'], data: Partial<Update>) {
const { data: items, error } = await table().update(data as any).eq('id', id).select('*');
if (error) {
throw error;
}
const [item] = items;
if (!item) {
throw new Error(`Could not save ${tableName}:${id}. Perhaps a problem with RLS?`);
}
Object.assign(this[tableName as any][id], item);
return this[tableName as any][id] as Row;
},
async delete(id: Row['id']) {
const { data: items, error } = await table().delete().eq('id', id).select('*');
if (items?.length === 0) {
throw new Error(`Could not delete ${tableName}:${id}. Perhaps a problem with RLS?`);
}
if (Number(items?.length) > 1) {
console.error(`Deleting ${tableName}:${id}`)
throw new Error('Something went wrong');
}
if (error) {
throw error;
}
delete this[tableName as any][id];
return;
},
async insert(data: Insert) {
const { data: items, error } = await table().insert(data as any).select('*');
if (error) {
throw error;
}
if (!items?.[0]) {
throw new Error(`Could not insert into ${tableName}. Perhaps a problem with RLS?`);
}
return this.add(items)[0];
},
async save(data: Insert | Update) {
if (data.id) {
return this.update(data.id as any, data);
}
return this.insert(data);
},
async query(cb: (filterBuilder: FilterBuilder) => FilterBuilder) {
const { data, error } = await cb(table().select('*'));
if (error) {
throw error;
}
return this.add(data as any[]);
},
async find(id: Row['id'], options?: { reload?: boolean }) {
const localEntity = this[tableName as any][id];
if (localEntity && !options?.reload) {
return localEntity as Row;
}
const { data, error } = await table().select('*').eq('id', id);
if (error) {
throw error;
}
if (!data[0]) {
throw new Error(`Could not find ${tableName}:${id}`);
}
return data[0];
},
peek(id: Row['id']) {
return this[tableName as any][id] as Row | undefined;
},
peekAll() {
return Object.values(this[tableName as any]) as Row[];
}
}
})
}
const useResourceStore = defineSupaStore(supabase, 'learning_resources');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment