Public
Like
glimpse2-runbook-view-glimpse-save-login-react
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.
Viewing readonly version of main branch: v27View latest version
Business logic, workflow orchestration, and HTTP handling.
Controllers are the business logic layer that:
- Implement business workflows and rules - Complex multi-step processes
- Orchestrate multiple service calls - Coordinate external operations
- Handle data transformation - Prepare data for different layers
- Manage error handling - Complex multi-step operation failures
- Use utility functions - Pure functions for data processing
- Handle environment variables - Application configuration
- Return structured data objects - Consistent response formats
Controllers do NOT:
- Make direct external API calls (use services instead)
- Implement pure utility functions (use utils instead)
- Handle HTTP routing concerns (routes handle this)
// Controller coordinates services and uses utils
export async function processAgentAssignment(pageId: string, personId: string) {
// 1. SERVICE: Get external data
const agentsResult = await findAgentsByPersonId(databaseId, personId);
// 2. UTIL: Transform/validate data
const cleanedAgents = validateAgentData(agentsResult.data);
// 3. SERVICE: Store processed data
const blobResult = await setAgentBlob(pageId, cleanedAgents);
// 4. BUSINESS LOGIC: Decide what to return
return {
success: true,
data: { agentsProcessed: cleanedAgents.length }
};
}
- Services: "I need to talk to Notion API" →
getPageById(pageId)
- Utils: "I need to format this data" →
formatAgentProperties(data)
- Controllers: "I need to implement this business workflow" → Orchestrate both
All controller functions return:
{
success: boolean,
data: any | null,
error: string | null,
details?: string // Additional error context when available
}
When to use: Most API endpoints, business logic that can be tested independently
Characteristics:
- Accept simple parameters (strings, numbers, objects)
- Return structured data objects
- Can be called from multiple contexts (HTTP, cron, etc.)
- Easy to unit test
Example: getDemoProperties(id: string)
export async function getDemoProperties(id: string) {
// 1. Validation (business rule)
if (!id) {
return { success: false, error: "Page ID is required", data: null };
}
// 2. SERVICE: Get external data
const result = await getPageById(id);
if (!result.success) {
return { success: false, error: result.error, data: null };
}
// 3. BUSINESS LOGIC: Filter sensitive data
const filteredProperties = Object.fromEntries(
Object.entries(result.data.properties).filter(
([_, property]) => property.type !== "button"
)
);
// 4. Return processed data
return {
success: true,
data: { ...result.data, properties: filteredProperties },
error: null
};
}
When to use: Authentication flows, webhooks, redirects, HTML responses
Characteristics:
- Accept Hono Context objects
- Return HTTP Response objects directly
- Handle HTTP-specific concerns (headers, redirects, HTML)
- Cannot be easily unit tested or reused
Example: Complex Webhook Handler
The /tasks/assign
webhook demonstrates sophisticated business logic orchestration:
export async function handleAssignWebhook(c: Context) {
// PHASE 1: Data Collection (No Side Effects)
// - Fetch all required data before making changes
// - Validate all data collection succeeded
// PHASE 2: Atomic Updates (All or Nothing)
// - Selective clearing based on business rules
// - Update Notion relations
// PHASE 3: Non-Critical Updates (Blob Storage)
// - Transform data for storage
// - Update blob (non-blocking)
}
The assignment webhook implements a sophisticated 3-phase workflow:
- Data Collection Phase: Fetch all agent data with no side effects
- Selective Clearing Logic: Only clear agents assigned to OTHER demos
- Atomic Updates: Coordinate multiple Notion operations
- Non-Critical Storage: Update blob storage without failing workflow
// Business logic: Decide whether to clear agent assignments
if (otherDemoAssignments.length > 0) {
console.log(`Agent has assignments to other demos - clearing needed`);
shouldClear = true;
} else if (currentDemoIds.includes(pageId)) {
console.log(`Agent already assigned to this demo only - no clearing needed`);
} else {
console.log(`Agent has no current assignments - no clearing needed`);
}
// Transform agent data for blob storage (business requirement)
const cleanedAgents = agentDataCollection.map(agentData => ({
agentId: agentData.agentId,
agentName: agentData.agentName,
agentEmail: agentData.agentEmail,
agentMeetUrl: agentData.agentMeetUrl,
agentPhone: agentData.agentPhone
// Exclude agentPageData (full Notion object) from storage
}));
// Business logic: Handle partial failures gracefully
if (!dataCollectionSuccess) {
console.error("Data collection failed, aborting to prevent inconsistent state");
return c.json({
success: false,
error: "Failed to collect complete agent data",
details: dataCollectionError
}, 500);
}
// Business logic: Extract agent properties from various Notion property types
function extractAgentProperties(agentPage: any) {
// Handle multiple property names and types
// Apply business rules for property extraction
// Return standardized agent data structure
}
- Page data retrieval and filtering
- Business rule: Remove button properties
- Data transformation for frontend consumption
- Complex webhook processing workflows
- Multi-phase assignment logic
- Selective clearing based on business rules
- Blob storage coordination
- Real-time viewing status management
- Session tracking and cleanup
- Notion synchronization coordination
- User authentication workflows
- Database user management
- URL validation and redirection logic
- HTML response generation for user flows
- Implement complex business processes as multi-phase operations
- Validate all required data before making changes
- Handle partial failures gracefully
- Provide detailed logging for debugging
- Use services for all external operations
- Use utils for all data transformation
- Implement business logic in controllers
- Return consistent response structures
- Provide user-friendly error messages
- Include technical details for debugging
- Handle edge cases and partial failures
- Log comprehensive error context
- Transform data according to business requirements
- Filter sensitive information before returning
- Validate data integrity and consistency
- Handle different data formats and structures