Skip to main content

Overview

The @budgetbee/core package is the heart of BudgetBee, providing essential functionality for authentication, database access, feature flags, and core business logic.

Installation

pnpm add @budgetbee/core --filter your-package
Or in your package.json:
{
  "dependencies": {
    "@budgetbee/core": "workspace:*"
  }
}

Package Information

{
  "name": "@budgetbee/core",
  "version": "1.0.0",
  "type": "module",
  "description": "Core package for budgetbee"
}

Exports

The package provides multiple entry points:
// Authentication (server-side)
import { auth } from "@budgetbee/core/auth";

// Authentication (client-side)
import { authClient } from "@budgetbee/core/auth-client";

// Database access
import { db, getDb } from "@budgetbee/core/db";
import { getAuthAdminClient } from "@budgetbee/core/db-pool";

// Feature flags
import { getFeatureFlag } from "@budgetbee/core/feature-flags";

// Error handling
import { CustomError } from "@budgetbee/core/error";

// UI components
import { Component } from "@budgetbee/core/ui/component";

Authentication

Server-Side Auth

BudgetBee uses Better Auth for authentication with several plugins:
import { auth } from "@budgetbee/core/auth";

// Get current session
const session = await auth.api.getSession({
  headers: request.headers
});

// Verify user is authenticated
if (!session?.user) {
  throw new Error('Unauthorized');
}

Configuration

The auth system is configured with:
  • Email & Password authentication
  • Email verification required
  • Organization support for multi-tenant features
  • JWT tokens for API access
  • Polar integration for subscription management
  • Role-based access control (RBAC)

Plugins

import {
  bearer,        // Bearer token authentication
  customSession, // Custom session fields
  jwt,           // JWT token support
  organization   // Organization management
} from "better-auth/plugins";

Client-Side Auth

import { authClient } from "@budgetbee/core/auth-client";

// Sign in
const { data, error } = await authClient.signIn.email({
  email: '[email protected]',
  password: 'password'
});

// Sign out
await authClient.signOut();

// Get current user
const { data: session } = await authClient.getSession();

// Get access token
const { data: token } = await authClient.token();

Email Verification

Email verification is required and handled automatically:
// Verification email is sent on signup
// Uses @budgetbee/email package with Resend

email: {
  enabled: true,
  requireEmailVerification: true,
  sendVerificationEmail: async (data) => {
    // Sends email via Resend
  }
}

Password Reset

// Request password reset
await authClient.forgetPassword({
  email: '[email protected]',
  redirectTo: '/reset-password'
});

// Reset password with token
await authClient.resetPassword({
  newPassword: 'newpassword',
  token: 'reset-token'
});

Database Access

PostgREST Client

BudgetBee uses PostgREST for database access via REST API:
import { db, getDb } from "@budgetbee/core/db";

// With existing token
const client = db(token);

// Automatically fetch token
const client = await getDb();

// Query database
const { data, error } = await client
  .from('transactions')
  .select('*')
  .eq('user_id', userId)
  .order('date', { ascending: false })
  .limit(10);

Query Examples

// Get all transactions
const { data } = await client
  .from('transactions')
  .select('*');

// Select specific columns
const { data } = await client
  .from('transactions')
  .select('id, amount, description');

// Join with categories
const { data } = await client
  .from('transactions')
  .select(`
    *,
    category:categories(*)
  `);

Admin Database Access

For operations requiring elevated privileges:
import { 
  getAuthAdminClient, 
  getSubscriptionAdminClient 
} from "@budgetbee/core/db-pool";

// Auth operations (users, sessions)
const authClient = getAuthAdminClient();
const result = await authClient.query(
  'SELECT * FROM users WHERE id = $1',
  [userId]
);

// Subscription operations
const subClient = getSubscriptionAdminClient();
const result = await subClient.query(
  'UPDATE subscriptions SET status = $1 WHERE id = $2',
  ['active', subscriptionId]
);
Use admin clients carefully - They bypass row-level security. Only use for privileged operations.

Feature Flags

BudgetBee includes a flexible feature flag system with multi-level scoping:
import { getFeatureFlag } from "@budgetbee/core/feature-flags";
import type { FeatureFlagScope } from "@budgetbee/core/feature-flags";

// Check global feature flag
const isEnabled = await getFeatureFlag('new-dashboard');

// Check account-specific flag
const isEnabled = await getFeatureFlag('beta-features', {
  accountId: user.id
});

// Check organization-specific flag
const isEnabled = await getFeatureFlag('custom-branding', {
  orgId: organization.id
});

// Check with full context
const isEnabled = await getFeatureFlag('advanced-analytics', {
  accountId: user.id,
  orgId: organization.id
});

Feature Flag Scopes

Scope Priority

Organization > Account > Global
Feature flags can be scoped at three levels:
  1. Global - Applies to all users
  2. Account - Specific to individual user accounts
  3. Organization - Specific to organizations

Caching

Feature flags are cached in Redis for 5 minutes:
// Cache key format
const cacheKey = `flag:${key}:org:${orgId}:acc:${accountId}`;

// TTL: 300 seconds (5 minutes)
await redis.setex(cacheKey, 300, String(value));

Permissions

BudgetBee implements role-based access control:
import { 
  accessControl,
  owner,
  admin,
  editor,
  viewer 
} from "@budgetbee/core/permissions";

Role Hierarchy

1

Owner

Full access to organization, can manage all members and settings
2

Admin

Can manage organization settings and members (except owner)
3

Editor

Can create and edit content, limited administrative access
4

Viewer

Read-only access to organization data

UI Components

The core package includes some UI components for auth flows:
import { SignInForm } from "@budgetbee/core/ui/sign-in-form";
import { SignUpForm } from "@budgetbee/core/ui/sign-up-form";
For general UI components, use @budgetbee/ui instead.

Dependencies

Key dependencies used by @budgetbee/core:
{
  "dependencies": {
    "@budgetbee/billing": "workspace:*",
    "@budgetbee/email": "workspace:*",
    "@polar-sh/better-auth": "1.6.4",
    "@supabase/postgrest-js": "^1.21.4",
    "better-auth": "1.4.17",
    "ioredis": "^5.8.2",
    "zod": "^4.3.6"
  }
}

Environment Variables

Required environment variables:
# Database
POSTGRES_USER=your_db_user
POSTGRES_PASSWORD=your_db_password
POSTGRES_HOST=localhost
POSTGRES_PORT=5432
POSTGRES_DB=budgetbee

# Admin Users
POSTGRES_AUTH_ADMIN_USER=auth_admin
POSTGRES_AUTH_ADMIN_PASSWORD=password
POSTGRES_SUBSCRIPTION_ADMIN_USER=sub_admin
POSTGRES_SUBSCRIPTION_ADMIN_PASSWORD=password

# PostgREST
NEXT_PUBLIC_PG_REST_URL=http://localhost:3000
PGRST_JWT_SECRET=your_jwks_secret

# Redis
REDIS_URL=redis://localhost:6379

# Application
NEXT_PUBLIC_APP_URL=http://localhost:3001
NEXT_PUBLIC_SITE_URL=http://localhost:3000

# Email (Resend)
RESEND_API_KEY=your_resend_api_key
SMTP_SENDER_NAME=BudgetBee
SMTP_MAIL=[email protected]

# Polar
POLAR_ACCESS_TOKEN=your_polar_token

Database Migrations

Migrations are stored in packages/core/migrations/:
packages/core/migrations/
├── better-auth-migrations.sql       # Auth tables
├── init.sql                         # Initial schema
├── migration_2024_01_15_*.sql      # Feature migrations
└── migration_2024_02_01_*.sql      # More migrations
Run migrations in order:
make migrate
Or manually:
./scripts/run_migrations.sh

Redis Configuration

Redis is used for caching and session storage:
import { redis } from "@budgetbee/core/redis";

// Get value
const value = await redis.get(key);

// Set with expiration
await redis.setex(key, 300, value);

// Delete
await redis.del(key);

Best Practices

Use getDb()

Prefer getDb() over db() to automatically handle token management

Check Permissions

Always verify user permissions before performing sensitive operations

Cache Feature Flags

Feature flags are automatically cached, don’t implement additional caching

Handle Errors

Always handle errors from database operations and auth calls

Troubleshooting

Database connection fails

Check environment variables:
echo $NEXT_PUBLIC_PG_REST_URL
echo $PGRST_JWT_SECRET
Ensure PostgREST is running:
cd infra
docker compose ps

Authentication not working

Verify JWKS secret is set:
  1. Visit http://localhost:3000/api/auth/jwks
  2. Copy the secret
  3. Set PGRST_JWT_SECRET in .env
  4. Restart containers

Feature flags not updating

Feature flags are cached for 5 minutes. Either:
  • Wait for cache to expire
  • Flush Redis: redis-cli FLUSHDB

Next Steps

UI Package

Explore the UI component library

Billing Package

Learn about subscription management

Build docs developers (and LLMs) love