FeaturesTemplatesShowcaseTownie
AI
BlogDocsPricing
Log inSign up
lightweight
lightweightglimpse2-runbook-view-glimpse-save-login-react
Remix of lightweight/glimpse2-runbook-view-glimpse-save-login
Public
Like
glimpse2-runbook-view-glimpse-save-login-react
Home
Code
8
_townie
13
backend
7
frontend
9
shared
3
.vtignore
README.md
deno.json
H
main.tsx
Branches
2
Pull requests
Remixes
History
Environment variables
6
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.
Sign up now
Code
/
backend
/
controllers
/
README.md
Code
/
backend
/
controllers
/
README.md
Search
9/10/2025
Viewing readonly version of main branch: v27
View latest version
README.md

Controllers

Business logic, workflow orchestration, and HTTP handling.

Separation of Concerns

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)

How Controllers Use Other Layers

Controllers Orchestrate Services and Utils

// 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 } }; }

Clear Layer Responsibilities

  • 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

Standard Response Structure

All controller functions return:

{ success: boolean, data: any | null, error: string | null, details?: string // Additional error context when available }

Controller Patterns

1. Data Controller Pattern (Recommended)

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 }; }

2. HTTP Handler Pattern (Use Sparingly)

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) }

Business Logic Examples

Complex Workflow Management

The assignment webhook implements a sophisticated 3-phase workflow:

  1. Data Collection Phase: Fetch all agent data with no side effects
  2. Selective Clearing Logic: Only clear agents assigned to OTHER demos
  3. Atomic Updates: Coordinate multiple Notion operations
  4. Non-Critical Storage: Update blob storage without failing workflow

Smart Decision Making

// 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`); }

Data Transformation

// 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 }));

Error Recovery Strategies

// 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); }

Property Extraction Logic

// 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 }

Controller Types

Demo Controllers (demo.controller.ts)

  • Page data retrieval and filtering
  • Business rule: Remove button properties
  • Data transformation for frontend consumption

Tasks Controllers (tasks.controller.ts)

  • Complex webhook processing workflows
  • Multi-phase assignment logic
  • Selective clearing based on business rules
  • Blob storage coordination

Viewing Controllers (viewing.controller.ts)

  • Real-time viewing status management
  • Session tracking and cleanup
  • Notion synchronization coordination

Glimpse Controllers (glimpse.controller.ts)

  • User authentication workflows
  • Database user management
  • URL validation and redirection logic
  • HTML response generation for user flows

Best Practices

Workflow Orchestration

  • Implement complex business processes as multi-phase operations
  • Validate all required data before making changes
  • Handle partial failures gracefully
  • Provide detailed logging for debugging

Service Coordination

  • Use services for all external operations
  • Use utils for all data transformation
  • Implement business logic in controllers
  • Return consistent response structures

Error Handling

  • Provide user-friendly error messages
  • Include technical details for debugging
  • Handle edge cases and partial failures
  • Log comprehensive error context

Data Management

  • Transform data according to business requirements
  • Filter sensitive information before returning
  • Validate data integrity and consistency
  • Handle different data formats and structures
Go to top
X (Twitter)
Discord community
GitHub discussions
YouTube channel
Bluesky
Product
FeaturesPricing
Developers
DocsStatusAPI ExamplesNPM Package Examples
Explore
ShowcaseTemplatesNewest ValsTrending ValsNewsletter
Company
AboutBlogCareersBrandhi@val.town
Terms of usePrivacy policyAbuse contact
© 2025 Val Town, Inc.