Unified Backend Architecture
Comprehensive guide to the consolidated backend architecture, including all services, authentication, database, and deployment.
Unified Backend Architecture
This guide provides a comprehensive overview of the consolidated backend architecture (apps/ai), which unifies all backend services into a single, cohesive Express application.
Architecture Overview
The unified backend serves as the central hub for all application services, providing a single deployment target while maintaining clear separation of concerns through route modules.
graph TB
subgraph "Client Layer"
Native[Native App]
Web[Web Apps]
end
subgraph "API Gateway Layer"
Express[Express Server<br/>Port 3005]
end
subgraph "Route Modules"
AI[AI Routes<br/>/api/ai]
Move[Move Routes<br/>/api/move]
IPFS[IPFS Routes<br/>/api/ipfs]
DB[Database Routes<br/>/api/database]
Notif[Notifications Routes<br/>/api/notifications]
Auth[Auth Routes<br/>/api/auth]
HB[Heartbeat Routes<br/>/api/heartbeat]
end
subgraph "External Services"
OpenAI[OpenAI API]
Movement[Movement Testnet]
Helia[Helia IPFS Node]
ExpoPush[Expo Push Service]
Privy[Privy Auth Service]
end
subgraph "Data Layer"
Postgres[(PostgreSQL)]
end
Native --> Express
Web --> Express
Express --> AI
Express --> Move
Express --> IPFS
Express --> DB
Express --> Notif
Express --> Auth
Express --> HB
AI --> OpenAI
Move --> Movement
IPFS --> Helia
Notif --> ExpoPush
Auth --> Privy
DB --> Postgres
AI --> Postgres
Notif --> Postgres
Auth --> Postgres
Core Components
Express Server
- Port: 3005 (configurable via
PORT) - Framework: Express with TypeScript
- Middleware: CORS, JSON parsing, timing utilities, authentication
- Route Structure: Modular routes under
/api/*prefixes
Database Layer
- Engine: PostgreSQL with connection pooling
- ORM: Drizzle with type-safe queries
- Security: Row Level Security (RLS) for multi-tenant data isolation
- Migrations: Automated schema management
Authentication
- Provider: Privy (server-side token validation)
- Middleware:
validateAuthfor route protection - Scope: User-based data access across all services
Performance Monitoring
- Timing Utilities: Request/response timing and logging
- Middleware: Automatic performance tracking for all routes
- Storage: Timing data persistence for analysis
Service Integration
How Services Work Together
-
User Registration Flow:
App → /api/auth/register → Privy validation → Database user creation → Device registration → Response -
AI Chat with Data Persistence:
App → /api/ai/chat → OpenAI streaming → /api/database/entries → User data storage → Response stream -
Push Notifications:
App → /api/notifications/devices/register → Token storage → /api/notifications/send → Expo service → Device delivery -
Blockchain + Storage:
App → /api/ipfs/upload → CID generation → /api/move/submit-transaction → On-chain storage → Confirmation
Environment Configuration
Required Variables
# OpenAI Configuration
OPENAI_API_KEY=sk-your-openai-key
# Privy Authentication
PRIVY_APP_ID=your-privy-app-id
PRIVY_APP_SECRET=your-privy-app-secret
# Database Connection
DATABASE_URL=postgresql://user:password@localhost:5432/cubed
# Server Configuration
PORT=3005
HOST=0.0.0.0
NODE_ENV=production
# Optional: IPFS API Key
IPFS_API_KEY=your-ipfs-api-key
Development vs Production
Development (NODE_ENV=development):
- Local PostgreSQL database
- Console logging enabled
- Detailed error responses
- Host:
localhost
Production (NODE_ENV=production):
- Production database (e.g., Fly.io PostgreSQL)
- Structured logging
- Minimal error details
- Host:
0.0.0.0
Startup Sequence
The backend follows a specific initialization order:
// 1. Environment validation
console.log('[AI Backend] Script loading...');
// 2. Express app setup
const app = express();
app.use(cors());
app.use(express.json());
app.use(timingMiddleware()); // Performance tracking
// 3. Route mounting
app.use('/api/ai', aiRoutes);
app.use('/api/move', moveRoutes);
// ... other routes
// 4. Helia IPFS initialization (async)
await initializeHelia();
// 5. Activity service startup
activityService.start();
// 6. Server start
app.listen(port, host, () => {
console.log(`✅ AI Server running at http://${host}:${port}`);
});
Database Schema & Migrations
Schema Overview
-- Users table (Privy integration)
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
privyUserId TEXT UNIQUE NOT NULL,
createdAt TIMESTAMP DEFAULT NOW()
);
-- Devices table (multi-device support)
CREATE TABLE devices (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
userId UUID REFERENCES users(id) ON DELETE CASCADE,
deviceName TEXT,
deviceModel TEXT,
osVersion TEXT,
platform TEXT CHECK (platform IN ('ios', 'android')),
appVersion TEXT,
buildVersion TEXT,
deviceType INTEGER,
platformApiLevel INTEGER,
isPhysicalDevice BOOLEAN,
osName TEXT,
deviceYearClass INTEGER,
totalMemory BIGINT,
isActive BOOLEAN DEFAULT true,
createdAt TIMESTAMP DEFAULT NOW(),
updatedAt TIMESTAMP DEFAULT NOW()
);
-- Device tokens (push notifications)
CREATE TABLE deviceTokens (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
deviceId UUID REFERENCES devices(id) ON DELETE CASCADE,
userId UUID REFERENCES users(id) ON DELETE CASCADE,
pushToken TEXT UNIQUE NOT NULL,
isActive BOOLEAN DEFAULT true,
lastUsedAt TIMESTAMP DEFAULT NOW(),
createdAt TIMESTAMP DEFAULT NOW(),
updatedAt TIMESTAMP DEFAULT NOW()
);
-- User entries (data sync)
CREATE TABLE entries (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
userId UUID REFERENCES users(id) ON DELETE CASCADE,
content TEXT NOT NULL,
createdAt TIMESTAMP DEFAULT NOW(),
updatedAt TIMESTAMP DEFAULT NOW(),
deleted BOOLEAN DEFAULT false
);
Migration Commands
# Generate migrations from schema changes
cd apps/ai
pnpm db:generate
# Apply migrations to database
pnpm db:push
# View current schema
pnpm db:studio
API Endpoints Reference
Authentication Routes (/api/auth)
| Method | Endpoint | Description | Auth Required |
|--------|----------|-------------|---------------|
| POST | /register | Register user with device info | Privy token |
| POST | /logout | Deactivate all user devices | Privy token |
Database Routes (/api/database)
| Method | Endpoint | Description | Auth Required |
|--------|----------|-------------|---------------|
| GET | /entries | Fetch user entries (with ?since filter) | Yes |
| POST | /entries | Create/update entries (upsert) | Yes |
AI Routes (/api/ai)
| Method | Endpoint | Description | Auth Required |
|--------|----------|-------------|---------------|
| POST | /chat | Streaming AI conversation | Yes |
Move Routes (/api/move)
| Method | Endpoint | Description | Auth Required |
|--------|----------|-------------|---------------|
| POST | /generate-hash | Generate transaction hash | Yes |
| POST | /submit-transaction | Submit signed transaction | Yes |
| POST | /faucet | Request test tokens | Yes |
| GET | /balance/:address | Get MOVE balance | Yes |
| GET | /account-info/:address | Get account info | Yes |
| POST | /view | Call view function | Yes |
| POST | /get-resource | Get account resource | Yes |
IPFS Routes (/api/ipfs)
| Method | Endpoint | Description | Auth Required |
|--------|----------|-------------|---------------|
| POST | /upload-image | Upload image to IPFS | Yes + API key |
| POST | /upload-json | Upload JSON data | Yes + API key |
| POST | /upload | Legacy upload (name/desc/image) | Yes + API key |
| GET | /data/:cid | Retrieve JSON data | No |
| GET | /image/:cid | Retrieve image | No |
Notifications Routes (/api/notifications)
| Method | Endpoint | Description | Auth Required |
|--------|----------|-------------|---------------|
| POST | /devices/register | Register push token | Yes |
| DELETE | /devices/:tokenId | Deactivate token | Yes |
| GET | /devices | List user devices | Yes |
| POST | /send | Send to multiple users | Yes |
| POST | /send-to-token | Send to specific token | Yes |
Heartbeat Route (/api/heartbeat)
| Method | Endpoint | Description | Auth Required |
|--------|----------|-------------|---------------|
| POST | / | Update activity status | Yes |
Performance Tracking
Timing Middleware
All routes include automatic performance tracking:
// Automatic timing for every request
app.use(timingMiddleware());
// Example timing output:
// [timing] GET /api/database/entries - 45ms
// [timing] POST /api/move/generate-hash - 120ms
Custom Timing
For complex operations:
import { timeAndLog } from './utils/timing-utils.js';
const result = await timeAndLog(
'custom_operation',
async () => performComplexTask(),
{ userId, operationType: 'sync' }
);
Authentication Flow
Privy Token Validation
// Middleware validates tokens on protected routes
const validateAuth = async (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith('Bearer ')) {
return res.status(401).json({ error: 'Missing token' });
}
try {
const token = authHeader.split(' ')[1];
const verifiedClaims = await privy.utils().auth().verifyAccessToken(token);
req.userId = verifiedClaims.user_id;
next();
} catch (error) {
res.status(401).json({ error: 'Invalid token' });
}
};
Row Level Security
Database queries automatically scope to authenticated users:
-- Automatic user filtering
SELECT * FROM entries WHERE userId = $1; -- $1 is req.userId
Deployment Considerations
Fly.io Configuration
# fly.toml
app = "cubed-backend"
primary_region = "ord"
[build]
dockerfile = "Dockerfile"
[env]
PORT = "3000"
NODE_ENV = "production"
[services]
internal_port = 3000
protocol = "tcp"
[[services.ports]]
port = 80
handlers = ["http"]
force_https = true
[[services.ports]]
port = 443
handlers = ["tls", "http"]
Database Setup
# Attach PostgreSQL to Fly app
fly postgres create --name cubed-db
fly postgres attach cubed-db --app cubed-backend
# Run migrations
fly ssh console --app cubed-backend
pnpm db:push
Environment Secrets
# Set production secrets
fly secrets set OPENAI_API_KEY="sk-..."
fly secrets set PRIVY_APP_ID="..."
fly secrets set PRIVY_APP_SECRET="..."
fly secrets set DATABASE_URL="postgresql://..."
Monitoring & Debugging
Health Checks
# Health endpoint (no auth required)
GET /health
# Returns: { "status": "ok" }
Logs
# View application logs
fly logs --app cubed-backend
# View database logs
fly postgres logs --app cubed-db
Performance Analysis
Timing logs help identify bottlenecks:
[timing] POST /api/ai/chat - 2.3s (streaming response)
[timing] GET /api/database/entries - 45ms
[timing] POST /api/move/generate-hash - 120ms
Best Practices
Error Handling
- Use consistent error response format
- Log errors with context but don't expose sensitive details
- Implement graceful degradation for external service failures
Security
- Always validate input data
- Use parameterized queries to prevent SQL injection
- Rotate API keys regularly
- Implement rate limiting for production
Performance
- Use connection pooling for database
- Implement caching where appropriate
- Monitor memory usage and implement cleanup routines
- Use streaming for large data transfers
Scalability
- Design for horizontal scaling
- Use database indexes for common queries
- Implement background job processing for heavy operations
- Monitor service limits and implement queuing
Troubleshooting
Common Issues
Database Connection Failed
- Verify
DATABASE_URLformat - Check PostgreSQL service status
- Ensure proper SSL configuration for production
Privy Token Validation Errors
- Confirm
PRIVY_APP_IDandPRIVY_APP_SECRETare set - Verify token format in Authorization header
- Check token expiration
IPFS Upload Failures
- Ensure Helia node is initialized (check startup logs)
- Verify API key is provided for uploads
- Check available disk space
Push Notification Issues
- Confirm Expo push tokens are registered
- Verify device platform information
- Check Expo service status
Debug Mode
Enable detailed logging in development:
NODE_ENV=development DEBUG=* pnpm start:ai
This provides comprehensive request/response logging and error details.
