FeaturesTemplatesShowcaseTownie
AI
BlogDocsPricing
Log inSign up
lightweight
lightweightglimpse2-runbook
Public
Like
glimpse2-runbook
Home
Code
4
_townie
13
.vtignore
deno.json
main.tsx
Branches
2
Pull requests
Remixes
3
History
Environment variables
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
/
_townie
/
04-view.md
Code
/
_townie
/
04-view.md
Search
8/6/2025
Viewing readonly version of main branch: v139
View latest version
04-view.md

Instructions for Adding /views/glimpse/:id Endpoint

Here are the step-by-step instructions that would lead directly to the correct, clean implementation:

1. Analyze the Requirements

  • Endpoint: GET /views/glimpse/:id
  • Authentication: Required (existing Google OAuth)
  • Input: Notion page ID (not database ID)
  • Output: JSON response with full service response (minus UI elements)
  • Data source: Notion page properties

2. Examine Current Architecture

Check existing services to understand data access patterns

cat /backend/services/notion.service.ts

Check existing routes structure

cat /backend/routes/views/\_views.routes.ts

Understand authentication flow

cat /main.tsx # Look for auth middleware setup

3. Identify Missing Service Method

  • Current service has getDatabaseById() for database metadata
  • Need getPageById() for individual page data
  • Service should return full Notion response for flexibility

4. Add Service Method First

// Add to /backend/services/notion.service.ts
export async function getPageById(pageId: string) {
  try {
    const response = await notion.pages.retrieve({
      page_id: pageId,
    });
    return {
      success: true,
      data: response,
      timestamp: new Date().toISOString(),
    };
  } catch (error) {
    return {
      success: false,
      error: error.message,
      timestamp: new Date().toISOString(),
    };
  }
}

5. Create Controller with Minimal Processing

// Create /backend/controllers/glimpse.controller.ts
import { Context } from "npm:hono@3.12.12";
import { getPageById } from "../services/notion.service.ts";

export async function glimpseHandler(c: Context) {
const id = c.req.param("id");

if (!id) {
  return c.json({ error: "Demo ID is required" }, 400);
}

const result = await getPageById(id);

if (!result.success) {
  return c.json({ error: "Failed to fetch demo data", details: result.error }, 500);
}

// Filter out button properties from the response
if (result.data?.properties) {
  const filteredProperties = Object.fromEntries(
  Object.entries(result.data.properties).filter(([key, value]) => value?.type !== "button")
);

    result.data.properties = filteredProperties;

  }

  return c.json(result);
}

6. Add Route

// Update /backend/routes/views/\_views.routes.ts
import { Hono } from "npm:hono@3.12.12";
import { glimpseHandler } from "../../controllers/glimpse.controller.ts";

const app = new Hono();

app.get("/glimpse/:id", glimpseHandler);

export default app;

7. Update Documentation

Add to /backend/routes/views/README.md

GET /views/glimpse/:id

  • Purpose: Returns Notion page data as JSON (filtered for data consumption)
  • Authentication: Required (Google OAuth)
  • Parameters: id - Notion page ID
  • Response: Service response object with Notion page data (button properties removed)
  • Filtering: Removes UI-specific properties (type: "button") for cleaner data consumption

8. Key Design Principles

✅ Controller Best Practices:

  • Single Responsibility: Controller handles HTTP concerns + minimal data cleanup
  • Thin Layer: Minimal processing, focused on improving data consumption
  • Useful Filtering: Remove UI elements that don't belong in data APIs
  • Consistent Error Handling: Standard error response format

✅ Service Layer:

  • Data Access: Handle all Notion API interactions
  • Consistent Response Format: Always return {success, data/error, timestamp}
  • Raw Data: Return unfiltered data from external APIs

✅ Separation of Concerns:

  • Controller: HTTP validation, routing, error responses, basic data cleanup
  • Service: External API calls, data retrieval
  • Client: Complex data processing, extraction, formatting

9. Response Structure

{ "success": true, "data": { "object": "page", "id": "notion-page-id", "properties": { "Name": { "type": "title", "title": [{"plain_text": "Demo Name"}] }, "Status": { "type": "select", "select": {"name": "Active"} } // Button properties filtered out } }, "timestamp": "2025-07-15T17:25:00.000Z" }

10. Key Decision Points

✅ Correct Approach:

  • Data Source: Use notion.pages.retrieve() for page data
  • Controller Logic: Keep minimal but include useful data cleanup
  • Property Filtering: Remove UI-specific elements (buttons) that don't belong in data APIs
  • Error Handling: Consistent service response pattern

✅ Appropriate Controller Processing:

  • UI Element Removal: Filter out button properties (not data)
  • Security Cleanup: Remove potentially sensitive UI configurations
  • Performance: Reduce payload size by removing unnecessary properties

❌ Avoid These Mistakes:

  • Over-processing: Don't extract/transform business data in controller
  • Tight Coupling: Don't assume specific property names or structures
  • Inconsistent Responses: Don't create custom response formats
  • Mixed Responsibilities: Don't mix HTTP handling with complex business logic

11. Testing Strategy

Test authentication (should show login page)

curl /views/glimpse/test-id

Test with valid Notion page ID (after auth)

curl -H "Cookie: auth-cookie" /views/glimpse/actual-notion-page-id

Verify button properties are filtered out

Response should not contain any properties with "type": "button"

12. Benefits of This Approach

  • Clean Data: Removes UI clutter from data API responses
  • Maintainable: Clear separation between layers with focused responsibilities
  • Flexible: Client can still extract any business data from Notion response
  • Consistent: Follows existing service response patterns
  • Secure: Prevents exposure of UI configurations
  • Performance: Smaller payloads without unnecessary UI properties
  • Future-proof: Easy to extend filtering logic for other UI property types

13. When to Add Controller Processing

✅ Good reasons to process in controller:

  • Remove UI-specific elements (buttons, formulas for display)
  • Filter sensitive configuration data
  • Remove properties that are never useful for data consumption
  • Basic security/privacy filtering

❌ Avoid processing in controller:

  • Complex business logic transformations
  • Data extraction that depends on specific use cases
  • Heavy computational processing
  • Application-specific data formatting
  • This approach creates a clean, maintainable endpoint that provides useful data filtering while maintaining architectural best practices and keeping the controller focused on its core responsibilities.
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.