This document describes the database schema validation system for ensuring the Creditra backend has all required tables, columns, and indexes before serving traffic.
The schema validation CLI (npm run db:validate) performs comprehensive checks on the PostgreSQL database to ensure:
- All required tables exist (borrowers, credit_lines, risk_evaluations, transactions, events)
- Critical columns exist in each table
- Performance-critical indexes are present
This is designed for use as a Kubernetes init container or pre-flight check before starting the application.
DATABASE_URL=postgresql://user:pass@localhost:5432/creditra npm run db:validate# Skip column validation (faster, less strict)
SKIP_COLUMN_CHECK=true npm run db:validate
# Skip index validation
SKIP_INDEX_CHECK=true npm run db:validate
# Skip both (tables only)
SKIP_COLUMN_CHECK=true SKIP_INDEX_CHECK=true npm run db:validate0- Validation passed, database is ready1- Validation failed or connection error
Verifies all core tables exist:
borrowers- Borrower identities and wallet addressescredit_lines- Credit facilities per borrowerrisk_evaluations- Historical risk scores and termstransactions- Draws and repaymentsevents- Immutable domain events
Checks that critical columns exist in each table:
borrowers:
id,wallet_address,created_at
credit_lines:
id,borrower_id,credit_limit,currency,status,created_at
risk_evaluations:
id,borrower_id,risk_score,suggested_limit,interest_rate_bps,evaluated_at
transactions:
id,credit_line_id,type,amount,currency,created_at
events:
id,event_type,created_at
Verifies performance-critical indexes exist:
borrowers:
borrowers_wallet_address_key- Unique constraint on wallet addresses
credit_lines:
credit_lines_borrower_id_idx- Foreign key lookupcredit_lines_status_idx- Status filtering
risk_evaluations:
risk_evaluations_borrower_id_idx- Foreign key lookup
transactions:
transactions_credit_line_id_idx- Foreign key lookup
events:
events_idempotency_key_key- Deduplication
Use as an init container to ensure database is ready before starting the main application:
apiVersion: apps/v1
kind: Deployment
metadata:
name: creditra-backend
spec:
template:
spec:
initContainers:
- name: db-validate
image: creditra-backend:latest
command: ["npm", "run", "db:validate"]
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: creditra-secrets
key: database-url
containers:
- name: api
image: creditra-backend:latest
# ... main container configFor ongoing health checks, use a readiness probe:
readinessProbe:
exec:
command: ["npm", "run", "db:validate"]
initialDelaySeconds: 5
periodSeconds: 30
timeoutSeconds: 10
failureThreshold: 3import { getConnection } from './db/client.js';
import { validateSchema, SchemaValidationError } from './db/validate-schema.js';
async function startServer() {
const client = getConnection();
try {
await client.connect();
await validateSchema(client);
console.log('Database schema validated');
// Start Express server
app.listen(3000);
} catch (error) {
if (error instanceof SchemaValidationError) {
console.error('Schema validation failed:', error.message);
console.error('Details:', error.details);
process.exit(1);
}
throw error;
} finally {
await client.end();
}
}import { missingTables, missingColumns, missingIndexes } from './db/validate-schema.js';
// Check specific tables
const missing = await missingTables(client, ['borrowers', 'credit_lines']);
if (missing.length > 0) {
console.error('Missing tables:', missing);
}
// Check specific columns
const missingCols = await missingColumns(client, 'borrowers', ['id', 'wallet_address']);
if (missingCols.length > 0) {
console.error('Missing columns:', missingCols);
}
// Check specific indexes
const missingIdxs = await missingIndexes(client, 'borrowers', ['borrowers_wallet_address_key']);
if (missingIdxs.length > 0) {
console.error('Missing indexes:', missingIdxs);
}❌ Schema validation failed
ERROR: Missing tables: credit_lines, transactions
Missing tables:
- credit_lines
- transactions
Action required:
1. Ensure migrations have been applied: npm run db:migrate
2. Check migration files in migrations/ directory
3. Verify database schema matches expected structure
❌ Schema validation failed
ERROR: Missing required columns: borrowers.wallet_address, credit_lines.status
Missing columns:
- borrowers.wallet_address
- credit_lines.status
Action required:
1. Ensure migrations have been applied: npm run db:migrate
2. Check migration files in migrations/ directory
3. Verify database schema matches expected structure
❌ Schema validation failed
ERROR: Missing critical indexes: borrowers.borrowers_wallet_address_key
Missing indexes:
- borrowers.borrowers_wallet_address_key
Action required:
1. Ensure migrations have been applied: npm run db:migrate
2. Check migration files in migrations/ directory
3. Verify database schema matches expected structure
name: Deploy
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Validate database schema
env:
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: npm run db:validate
- name: Deploy
run: ./deploy.shvalidate-schema:
stage: test
script:
- npm ci
- npm run build
- npm run db:validate
variables:
DATABASE_URL: $DATABASE_URL
only:
- main
- stagingError: Cannot connect to database
Solutions:
- Verify
DATABASE_URLis set correctly - Check database server is running
- Verify network connectivity
- Check firewall rules
Error: Missing tables: borrowers, credit_lines, ...
Solutions:
- Run migrations:
npm run db:migrate - Check migration files exist in
migrations/directory - Verify
schema_migrationstable exists
Error: Missing required columns: borrowers.wallet_address
Solutions:
- Check migration file includes the column definition
- Verify migration was applied:
SELECT * FROM schema_migrations - Manually inspect table:
\d borrowers(psql)
Error: Missing critical indexes: borrowers_wallet_address_key
Solutions:
- Check migration file includes the index creation
- Manually create index if needed:
CREATE UNIQUE INDEX borrowers_wallet_address_key ON borrowers (wallet_address);
- Table checks: ~10ms per table
- Column checks: ~20ms per table
- Index checks: ~20ms per table
- Total: ~250ms for full validation
For faster validation in development:
# Skip expensive checks
SKIP_COLUMN_CHECK=true SKIP_INDEX_CHECK=true npm run db:validateFor production, always run full validation.
- Never commit
DATABASE_URLto version control - Use environment variables or secrets management
- Rotate credentials regularly
- Validation does not read table data, only schema metadata
- No PII is logged or exposed
- Safe to run in production
- Schema validation does not interact with Stellar network
- No private keys are used or required
- Only database schema is checked
npm test -- validate-schemaCoverage: 95%+ on all validation functions
Test against real PostgreSQL:
# Start test database
docker run -d --name test-db -e POSTGRES_PASSWORD=test -p 5433:5432 postgres:15
# Run validation
DATABASE_URL=postgresql://postgres:test@localhost:5433/postgres npm run db:validate
# Cleanup
docker rm -f test-db- Validate column types and constraints
- Check foreign key relationships
- Verify trigger existence
- Validate function/procedure definitions
- Schema drift detection
- Automated remediation suggestions