Skip to main content

Helper Libraries Reference

Documentation for utility functions in the lib/ directory.

Table of Contents


lib/supabase.ts

Comprehensive Supabase helper functions for database operations.

Exports


Supabase Client

Export:

export const supabase: SupabaseClient<Database>

Usage:

import { supabase } from '@/lib/supabase';

// Direct query (when helper doesn't exist)
const { data, error } = await supabase
.from('accounts')
.select('*')
.eq('user_id', userId);

Note: Prefer using helper functions over direct queries for type safety and consistency.


Authentication Functions

getCurrentUser()

Get current authenticated user with profile data.

Signature:

function getCurrentUser(): Promise<User | null>

Returns: User object or null if not authenticated

Example:

import { getCurrentUser } from '@/lib/supabase';

const user = await getCurrentUser();
if (!user) {
// Redirect to login
router.push('/finance/login');
return;
}

console.log(user.email, user.full_name);

Implementation:

  1. Gets auth user from Supabase Auth
  2. Fetches user profile from users table
  3. Returns combined user object

signIn()

Sign in user with email and password.

Signature:

function signIn(
email: string,
password: string
): Promise<{ user: User | null; error: string | null }>

Parameters:

  • email - User email
  • password - User password

Returns: Object with user (on success) or error (on failure)

Example:

import { signIn } from '@/lib/supabase';

const { user, error } = await signIn('user@example.com', 'password123');

if (error) {
setError(error);
return;
}

// User signed in successfully
router.push('/finance');

signUpWithInvite()

Sign up new user with invite code.

Signature:

function signUpWithInvite(
email: string,
password: string,
fullName: string,
inviteCode: string
): Promise<{ user: User | null; error: string | null }>

Parameters:

  • email - User email
  • password - User password
  • fullName - User's full name
  • inviteCode - Valid invite code

Returns: Object with user or error

Example:

import { signUpWithInvite } from '@/lib/supabase';

const { user, error } = await signUpWithInvite(
'newuser@example.com',
'securepassword',
'John Doe',
'WELCOME2024'
);

if (error) {
if (error.includes('expired')) {
setError('Your invite code has expired.');
} else if (error.includes('used')) {
setError('This invite code has been fully used.');
} else {
setError(error);
}
return;
}

// User created successfully

Validation Checks:

  1. ✅ Invite code exists and is active
  2. ✅ Not expired
  3. ✅ Usage limit not exceeded
  4. ✅ Creates auth user
  5. ✅ Updates invite usage count
  6. ✅ Links user to invite

signOut()

Sign out current user.

Signature:

function signOut(): Promise<{ error: string | null }>

Returns: Object with error if sign out failed

Example:

import { signOut } from '@/lib/supabase';

const handleSignOut = async () => {
const { error } = await signOut();

if (error) {
console.error('Sign out failed:', error);
return;
}

router.push('/finance/login');
};

isAdmin()

Check if user has admin privileges.

Signature:

function isAdmin(userId: string): Promise<boolean>

Parameters:

  • userId - User ID to check

Returns: true if user is admin, false otherwise

Example:

import { isAdmin } from '@/lib/supabase';

const checkAdminAccess = async () => {
const user = await getCurrentUser();
if (!user) return false;

const hasAdminAccess = await isAdmin(user.id);

if (!hasAdminAccess) {
toast.error('Admin access required');
return false;
}

return true;
};

Account Functions

getUserAccounts()

Get user's bank accounts with Plaid item info (optimized query).

Signature:

function getUserAccounts(userId: string): Promise<Account[]>

Parameters:

  • userId - User ID

Returns: Array of accounts (sorted: cash accounts first)

Example:

import { getUserAccounts } from '@/lib/supabase';

const accounts = await getUserAccounts(user.id);

accounts.forEach(account => {
console.log(account.account_name, account.current_balance);

// Access nested Plaid item data
if (account.plaid_items) {
console.log('Bank:', account.plaid_items.institution_name);
console.log('Status:', account.plaid_items.status);
}
});

Performance Note:

  • ✅ Uses single query with nested relations (not multiple queries)
  • ✅ 50% faster than separate queries for accounts and Plaid items

getTotalBalance()

Get total balance across all user accounts.

Signature:

function getTotalBalance(userId: string): Promise<number>

Parameters:

  • userId - User ID

Returns: Total balance in cents

Example:

import { getTotalBalance, formatCurrency } from '@/lib/supabase';

const balanceInCents = await getTotalBalance(user.id);
const formatted = formatCurrency(balanceInCents);

console.log('Total Balance:', formatted); // "$1,234.56"

Optimization:

  • ✅ Only fetches current_balance column (no unnecessary data)
  • ✅ Performs sum on client (fast for reasonable number of accounts)

Transaction Functions

getUserTransactions()

Get user's transactions with pagination and filtering.

Signature:

function getUserTransactions(
userId: string,
options?: {
limit?: number;
offset?: number;
startDate?: string;
endDate?: string;
category?: string;
}
): Promise<Transaction[]>

Parameters:

  • userId - User ID
  • options.limit - Max results (default: 100)
  • options.offset - Pagination offset (default: 0)
  • options.startDate - Filter by start date (ISO string)
  • options.endDate - Filter by end date (ISO string)
  • options.category - Filter by category

Returns: Array of transactions (sorted by date, newest first)

Example:

import { getUserTransactions } from '@/lib/supabase';

// Get last 30 days of transactions
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);

const transactions = await getUserTransactions(user.id, {
startDate: thirtyDaysAgo.toISOString().split('T')[0],
limit: 50,
});

// Get transactions for specific category
const foodTransactions = await getUserTransactions(user.id, {
category: 'Food and Drink',
});

// Pagination
const page2 = await getUserTransactions(user.id, {
limit: 50,
offset: 50,
});

getSpendingByCategory()

Get spending totals grouped by category for a date range.

Signature:

function getSpendingByCategory(
userId: string,
startDate: string,
endDate: string
): Promise<Record<string, number>>

Parameters:

  • userId - User ID
  • startDate - Start date (ISO string, YYYY-MM-DD)
  • endDate - End date (ISO string, YYYY-MM-DD)

Returns: Object mapping category to total spending (in cents)

Example:

import { getSpendingByCategory, formatCurrency } from '@/lib/supabase';

const spending = await getSpendingByCategory(
user.id,
'2024-12-01',
'2024-12-31'
);

Object.entries(spending).forEach(([category, amount]) => {
console.log(`${category}: ${formatCurrency(amount)}`);
});

// Output:
// Food and Drink: $456.78
// Transportation: $123.45
// Shopping: $789.01

getTotalSpending()

Get total spending for a date range.

Signature:

function getTotalSpending(
userId: string,
startDate: string,
endDate: string
): Promise<number>

Parameters:

  • userId - User ID
  • startDate - Start date (ISO string)
  • endDate - End date (ISO string)

Returns: Total spending in cents

Example:

import { getTotalSpending, formatCurrency } from '@/lib/supabase';

const totalCents = await getTotalSpending(user.id, '2024-12-01', '2024-12-31');
const formatted = formatCurrency(totalCents);

console.log('December Spending:', formatted);

Budget Functions

getUserBudgets()

Get user's active budgets.

Signature:

function getUserBudgets(userId: string): Promise<Budget[]>

Parameters:

  • userId - User ID

Returns: Array of active budgets

Example:

import { getUserBudgets } from '@/lib/supabase';

const budgets = await getUserBudgets(user.id);

budgets.forEach(budget => {
const percentage = (budget.spent_amount / budget.amount) * 100;
console.log(`${budget.name}: ${percentage.toFixed(0)}% spent`);
});

createBudget()

Create a new budget.

Signature:

function createBudget(
userId: string,
name: string,
category: string,
amount: number,
period: 'weekly' | 'monthly' | 'yearly'
): Promise<{ budget: Budget | null; error: string | null }>

Parameters:

  • userId - User ID
  • name - Budget name
  • category - Transaction category to track
  • amount - Budget limit in cents
  • period - Budget period

Returns: Object with budget or error

Example:

import { createBudget, dollarsToCents } from '@/lib/supabase';

const { budget, error } = await createBudget(
user.id,
'Groceries',
'Food and Drink',
dollarsToCents(500), // $500
'monthly'
);

if (error) {
toast.error(error);
return;
}

toast.success('Budget created!');

updateBudgetSpending()

Update budget spent amount.

Signature:

function updateBudgetSpending(
budgetId: string,
spentAmount: number
): Promise<{ error: string | null }>

Parameters:

  • budgetId - Budget ID
  • spentAmount - New spent amount in cents

Returns: Object with error if update failed

Example:

import { updateBudgetSpending } from '@/lib/supabase';

const { error } = await updateBudgetSpending(budget.id, 45000); // $450

if (error) {
console.error('Failed to update budget:', error);
}

Plaid Item Functions

getUserPlaidItems()

Get user's Plaid items (bank connections).

Signature:

function getUserPlaidItems(userId: string): Promise<PlaidItem[]>

Parameters:

  • userId - User ID

Returns: Array of Plaid items

Example:

import { getUserPlaidItems } from '@/lib/supabase';

const items = await getUserPlaidItems(user.id);

items.forEach(item => {
console.log(`${item.institution_name}: ${item.status}`);
console.log(`Last synced: ${item.last_synced_at}`);
});

Utility Functions

formatCurrency()

Format cents to dollars with currency symbol.

Signature:

function formatCurrency(cents: number): string

Parameters:

  • cents - Amount in cents

Returns: Formatted currency string

Example:

import { formatCurrency } from '@/lib/supabase';

formatCurrency(123456); // "$1,234.56"
formatCurrency(1000); // "$10.00"
formatCurrency(-5000); // "-$50.00"

dollarsToCents()

Convert dollars to cents (safe for database storage).

Signature:

function dollarsToCents(dollars: number): number

Parameters:

  • dollars - Amount in dollars

Returns: Amount in cents (integer)

Example:

import { dollarsToCents } from '@/lib/supabase';

dollarsToCents(19.99); // 1999
dollarsToCents(100); // 10000
dollarsToCents(0.01); // 1

Note: Uses Math.round() to handle floating-point precision issues.


centsToDollars()

Convert cents to dollars.

Signature:

function centsToDollars(cents: number): number

Parameters:

  • cents - Amount in cents

Returns: Amount in dollars

Example:

import { centsToDollars } from '@/lib/supabase';

centsToDollars(1999); // 19.99
centsToDollars(10000); // 100
centsToDollars(1); // 0.01

formatDate()

Format date string to readable format.

Signature:

function formatDate(dateString: string): string

Parameters:

  • dateString - ISO date string

Returns: Formatted date (e.g., "Dec 4, 2024")

Example:

import { formatDate } from '@/lib/supabase';

formatDate('2024-12-04'); // "Dec 4, 2024"
formatDate('2024-01-15T10:30:00Z'); // "Jan 15, 2024"

formatRelativeTime()

Format date as relative time (e.g., "2 days ago").

Signature:

function formatRelativeTime(dateString: string): string

Parameters:

  • dateString - ISO date string

Returns: Relative time string

Example:

import { formatRelativeTime } from '@/lib/supabase';

formatRelativeTime('2024-12-04T10:00:00Z'); // "Today"
formatRelativeTime('2024-12-03T10:00:00Z'); // "Yesterday"
formatRelativeTime('2024-11-27T10:00:00Z'); // "1 week ago"
formatRelativeTime('2024-11-04T10:00:00Z'); // "1 month ago"

isValidUser()

Type guard to check if object is a valid User.

Signature:

function isValidUser(user: unknown): user is User

Parameters:

  • user - Object to check

Returns: true if valid User, false otherwise

Example:

import { isValidUser } from '@/lib/supabase';

const data = await someFunction();

if (isValidUser(data)) {
// TypeScript knows data is User
console.log(data.email, data.id);
} else {
console.error('Invalid user object');
}

lib/plaid.ts

Plaid API integration helpers.

Key Functions

  • createLinkToken(userId) - Generate Plaid Link token
  • exchangePublicToken(publicToken) - Exchange public token for access token
  • getAccounts(accessToken) - Fetch accounts from Plaid
  • syncTransactions(accessToken, cursor) - Sync transactions
  • getItem(accessToken) - Get Plaid item details

lib/admin-auth.ts

Admin authentication and authorization helpers.

Key Functions

  • createAdminUser(email, password, fullName) - Create admin user
  • authenticateAdmin(email, password) - Step 1 of login
  • verifyAdminTOTP(userId, code) - Step 2 of login (2FA)
  • createAdminSession(userId) - Create admin session
  • verifyAdminSession(token) - Verify session validity
  • deleteAdminSession(token) - Logout
  • setupAdminTOTP(userId) - Initialize 2FA
  • verifyTOTPSetup(userId, code) - Verify 2FA setup
  • logAdminAction(userId, action, ...) - Audit logging

See: Authentication for detailed authentication documentation.


lib/env.ts

Type-safe environment variable validation.

Usage

Import:

import { env } from '@/lib/env';

Access variables:

// Supabase
env.supabase.url
env.supabase.anonKey
env.supabase.serviceRoleKey

// Plaid
env.plaid.clientId
env.plaid.secret
env.plaid.env // 'sandbox' | 'development' | 'production'

// Encryption
env.encryption.key

Validation:

  • ✅ Validates all required variables on app startup
  • ✅ Throws helpful errors for missing/invalid values
  • ✅ Type-safe access (no string indexing)

Example:

import { env } from '@/lib/env';

// Type-safe access
const plaidClient = new PlaidApi(
new Configuration({
basePath: PlaidEnvironments[env.plaid.env],
baseOptions: {
headers: {
'PLAID-CLIENT-ID': env.plaid.clientId,
'PLAID-SECRET': env.plaid.secret,
},
},
})
);

lib/utils.ts

Generic utility functions (likely cn() for Tailwind classes).

cn()

Conditionally join class names.

Signature:

function cn(...inputs: ClassValue[]): string

Usage:

import { cn } from '@/lib/utils';

// Conditional classes
const buttonClass = cn(
'px-4 py-2 rounded',
isActive && 'bg-blue-500',
isDisabled && 'opacity-50'
);

// Merge conflicting classes
const mergedClass = cn(
'text-red-500',
someCondition && 'text-blue-500' // Blue wins if true
);

Next Steps: