Action | Type | Resolved On |
|---|---|---|
| Supabase Query Wrapper | refactoring | - - - |
Most Supabase queries follow an identical pattern with repetitive error handling. A wrapper function could reduce boilerplate and standardize error handling.
The same query pattern repeats throughout the lib files:
// Pattern seen 20+ times across the codebase
const result = await supabase
.from("Table")
.select("*")
.eq("field", value)
// ... more conditions
if (result.error) {
console.error("Error in Fetching X", result.error.message);
// GA - Error Handling (placeholder comment, never implemented)
return [];
} else {
// Process result.data
return processedData;
}
Create a query wrapper utility in src/lib/db.ts with standardized error handling.
// src/lib/db.ts
import { supabase } from "./supabase";
import type { PostgrestBuilder } from "@supabase/postgrest-js";
export async function executeQuery<T>(
query: PostgrestBuilder<T>,
operationName: string,
defaultValue: T
): Promise<T> {
const result = await query;
if (result.error) {
console.error(`Error in ${operationName}:`, result.error.message);
// TODO: Send to error tracking service (Sentry, etc.)
return defaultValue;
}
return result.data as T;
}
// src/lib/db.ts
export interface QueryResult<T> {
data: T;
error: string | null;
success: boolean;
}
export async function query<T>(
operation: () => Promise<{ data: T | null; error: { message: string } | null }>,
operationName: string
): Promise<QueryResult<T>> {
try {
const { data, error } = await operation();
if (error) {
console.error(`Error in ${operationName}:`, error.message);
return { data: null as T, error: error.message, success: false };
}
return { data: data as T, error: null, success: true };
} catch (err) {
const message = err instanceof Error ? err.message : "Unknown error";
console.error(`Exception in ${operationName}:`, message);
return { data: null as T, error: message, success: false };
}
}
// Usage
const { data, error, success } = await query(
() => supabase.from("Ideas").select("*").eq("project", project),
"Fetching Ideas"
);
if (!success) {
// Handle error - maybe show user notification
return [];
}
// src/lib/db.ts
export class QueryBuilder<T> {
private query: any;
private operationName: string;
constructor(query: any, operationName: string) {
this.query = query;
this.operationName = operationName;
}
async execute(): Promise<T | null> {
const result = await this.query;
if (result.error) {
console.error(`Error in ${this.operationName}:`, result.error.message);
return null;
}
return result.data;
}
async executeOrDefault(defaultValue: T): Promise<T> {
return (await this.execute()) ?? defaultValue;
}
}
// Usage
const ideas = await new QueryBuilder(
supabase.from("Ideas").select("*"),
"Fetching Ideas"
).executeOrDefault([]);
src/lib/supabase.tssrc/lib/monthly.tssrc/lib/yearly.tssrc/lib/weekly.ts