Savvi Studio

Troubleshooting Examples

Purpose: Code examples for common troubleshooting scenarios
Last Updated: 2024-11-28

Connection Issues

Testing Database Connection

# Check database is running
docker ps | grep postgres

# Test connection directly
psql -h localhost -p 54320 -U savvi -d savvi_studio -c "SELECT 1"

# Check connection string
echo $DATABASE_URL

Verifying Schema Loaded

# List all schemas
psql -h localhost -p 54320 -U savvi -d savvi_studio -c "\dn"

# List functions in schema
psql -h localhost -p 54320 -U savvi -d savvi_studio -c "\df my_schema.*"

# Check specific function
psql -h localhost -p 54320 -U savvi -d savvi_studio \
  -c "\df my_schema.my_function"

Generation Issues

Force Clean Regeneration

# Remove all generated files
rm -rf src/__generated__

# Regenerate from scratch
pnpm db:codegen

# With verbose output
pnpm db:codegen --verbose

Check Generated Files

# List all generated files
find src/__generated__ -name "*.ts"

# Check specific schema
ls -la src/__generated__/graph/

# View specific file
cat src/__generated__/graph/index.ts

Debug with Verbose Output

# See all introspection queries
pnpm db:codegen --schema graph --verbose 2>&1 | tee codegen.log

# Filter for specific information
pnpm db:codegen --verbose 2>&1 | grep -A5 "Error"
pnpm db:codegen --verbose 2>&1 | grep "SELECT"
pnpm db:codegen --verbose 2>&1 | grep "Writing"

Type Error Fixes

Update Function Calls After Type Changes

// Before: Old signature
await myFunction(client, { p_id: '123' });

// After: Type changed to bigint
await myFunction(client, { p_id: 123n });

Find All Call Sites

# Search for function usage
grep -r "myFunction" src/

# With line numbers
grep -rn "myFunction" src/

# Exclude generated files
grep -r "myFunction" src/ --exclude-dir="__generated__"

Handle Missing Parameters

// ❌ Missing required param
await createUser(client, { p_name: 'John' });

// ✅ All required params
await createUser(client, {
  p_email: 'john@example.com',
  p_name: 'John'
});

Validation Error Handling

Basic Error Handling

import { ZodError } from 'zod';

try {
  await myFunction(client, params);
} catch (error) {
  if (error instanceof ZodError) {
    console.error('Validation errors:', error.errors);
    // [{ path: ['p_email'], message: 'Invalid email', ... }]
  }
}

Detailed Error Inspection

import { ZodError } from 'zod';

try {
  await createUser(client, untrustedParams);
} catch (error) {
  if (error instanceof ZodError) {
    for (const issue of error.errors) {
      console.error(`Field: ${issue.path.join('.')}`);
      console.error(`Error: ${issue.message}`);
      console.error(`Expected: ${issue.expected}`);
      console.error(`Received: ${issue.received}`);
    }
  }
}

Type-Safe Error Handling

import { ZodError } from 'zod';
import { DatabaseError } from 'pg';

async function safeCreateUser(params: unknown) {
  try {
    return await withClient(async (client) => {
      return await createUser(client, params);
    });
  } catch (error) {
    if (error instanceof ZodError) {
      // Validation error - input was invalid
      throw new ValidationError('Invalid user data', error.errors);
    } else if (error instanceof DatabaseError) {
      // Database error - constraint violation, etc
      throw new DbOperationError('Failed to create user', error);
    }
    throw error;
  }
}

Import Error Fixes

Restart TypeScript Server

// VS Code: Open Command Palette
// Cmd/Ctrl + Shift + P
// Type: "TypeScript: Restart TS Server"
// Press Enter

Verify tsconfig.json

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
      "@db": ["./src/__generated__/index.ts"],
      "@db/*": ["./src/__generated__/*"]
    }
  }
}

Check Import Paths

// ✅ Correct imports
import { createResource } from '@db/graph';
import type { NodeData } from '@db/graph/types';
import { studio, auth, graph } from '@db';

// ❌ Wrong imports
import { createResource } from '@db/auth'; // Wrong schema
import { NodeData } from '@db/graph'; // Missing /types
import createResource from '@db/graph'; // Default import doesn't exist

CI/CD Troubleshooting

GitHub Actions Example

name: Codegen Check

on: [pull_request]

jobs:
  check:
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
      
      - name: Install dependencies
        run: pnpm install
      
      - name: Start database
        run: docker compose up -d postgres
      
      - name: Wait for database
        run: |
          until psql $DATABASE_URL -c "SELECT 1" > /dev/null 2>&1; do
            echo "Waiting for database..."
            sleep 1
          done
      
      - name: Run migrations
        run: pnpm db:reset
      
      - name: Generate code
        run: pnpm db:codegen
      
      - name: Verify generated code
        run: pnpm db:codegen --check
      
      - name: Type check
        run: pnpm tsc --noEmit
      
      - name: Run tests
        run: pnpm test

GitLab CI Example

codegen:
  image: node:18
  services:
    - postgres:15
  variables:
    DATABASE_URL: "postgresql://postgres:password@postgres:5432/test"
  before_script:
    - pnpm install
    - pnpm db:reset
  script:
    - pnpm db:codegen
    - pnpm db:codegen --check
    - pnpm tsc --noEmit

Mock Client for Testing

Basic Mock

import { vi } from 'vitest';
import type { PoolClient } from 'pg';

const mockClient = {
  query: vi.fn().mockResolvedValue({
    rows: [{ result: 123n }]
  })
} as unknown as PoolClient;

// Use in test
const result = await myFunction(mockClient, params);
expect(mockClient.query).toHaveBeenCalledWith(
  'SELECT my_function($1, $2)',
  [param1, param2]
);

Advanced Mock with Multiple Calls

import { vi } from 'vitest';

const mockClient = {
  query: vi.fn()
    .mockResolvedValueOnce({ rows: [{ id: 1n }] })
    .mockResolvedValueOnce({ rows: [{ id: 2n }] })
    .mockResolvedValueOnce({ rows: [] })
} as any;

// First call returns id: 1n
// Second call returns id: 2n  
// Third call returns empty array

Mock with Error

import { vi } from 'vitest';
import { DatabaseError } from 'pg';

const mockClient = {
  query: vi.fn().mockRejectedValue(
    new DatabaseError('unique constraint violation', 1, 'error')
  )
} as any;

// Test error handling
await expect(myFunction(mockClient, params)).rejects.toThrow();

Integration Test Patterns

Using withTestClient

import { describe, it, expect } from 'vitest';
import { withTestClient } from '@/test/utils';
import { createResource, getResource } from '@db/graph';

describe('resource operations', () => {
  it('creates and retrieves resource', async () => {
    await withTestClient(async (client) => {
      // Create resource
      const id = await createResource(client, {
        p_type_namespace: 'test.user',
        p_data: { name: 'John' }
      });
      
      expect(id).toBeDefined();
      
      // Retrieve resource
      const resource = await getResource(client, { p_id: id });
      expect(resource).toBeDefined();
      expect(resource?.data).toEqual({ name: 'John' });
    });
  });
});

Transaction Testing

import { withTransaction } from '@/lib/db';

describe('transactional operations', () => {
  it('rolls back on error', async () => {
    await expect(
      withTransaction(async (client) => {
        await createResource(client, { /* ... */ });
        throw new Error('Simulated error');
      })
    ).rejects.toThrow('Simulated error');
    
    // Verify rollback - resource should not exist
    await withTestClient(async (client) => {
      const count = await countResources(client);
      expect(count).toBe(0n);
    });
  });
});

Performance Debugging

Check Query Execution Time

import { performance } from 'perf_hooks';

const start = performance.now();
await myFunction(client, params);
const end = performance.now();
console.log(`Execution time: ${end - start}ms`);

Profile Multiple Operations

import { performance } from 'perf_hooks';

async function profileOperations() {
  const results: Record<string, number> = {};
  
  for (const op of operations) {
    const start = performance.now();
    await op.execute();
    results[op.name] = performance.now() - start;
  }
  
  console.table(results);
}

Database Query Logging

-- Enable query logging in PostgreSQL
ALTER DATABASE savvi_dev SET log_statement = 'all';
ALTER DATABASE savvi_dev SET log_duration = on;

-- View slow queries
SELECT * FROM pg_stat_statements
ORDER BY total_exec_time DESC
LIMIT 10;

Cache Issues

Clear and Regenerate

# Disable cache
pnpm db:codegen --no-cache

# Force regeneration (bypasses cache)
pnpm db:codegen --force

# Remove cache directory (if exists)
rm -rf .codegen-cache
pnpm db:codegen

IDE Not Updating

VS Code Troubleshooting

# 1. Regenerate code
pnpm db:codegen

# 2. Restart TypeScript server
# Cmd/Ctrl + Shift + P → "TypeScript: Restart TS Server"

# 3. Restart dev server
pnpm dev

# 4. If still not working, reload window
# Cmd/Ctrl + Shift + P → "Developer: Reload Window"

Check File Watchers

# Check if files are being watched
# macOS/Linux
lsof | grep "src/__generated__"

# Increase file watcher limit (Linux)
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

These examples demonstrate how to diagnose and fix common codegen issues.