View Routes

Server-side rendered views and data endpoints for frontend consumption.

Separation of Concerns

View routes handle:

  • Authentication-protected data access
  • JSON response formatting for authenticated users
  • Business logic delegation to controllers
  • Consistent error response formatting

Endpoints

GET /views/glimpse/:id

Purpose: Get Notion page data for authenticated users (filtered for data consumption)

Authentication: Required (Google OAuth via LastLogin)

Input Data:

// URL Parameters { id: string // Notion page UUID } // Authentication Context (from middleware) { userEmail: string, // Other auth context }

Processing Flow:

  1. Authentication middleware validates user
  2. Extract page ID from URL parameter
  3. Call getDemoProperties(id) controller
  4. Return filtered page data

Success Response (200):

{ id: string, properties: { // All properties except type: "button" (UI filtering) [propertyName: string]: { type: "title" | "rich_text" | "number" | "select" | "multi_select" | "date" | "checkbox" | "url" | "email" | "phone_number" | "formula" | "relation" | "rollup" | "created_time" | "created_by" | "last_edited_time" | "last_edited_by", // Property-specific value structure based on type id: string, // Type-specific content } }, parent: { type: "database_id" | "page_id", database_id?: string, page_id?: string }, created_time: string, last_edited_time: string, created_by: { object: "user", id: string }, last_edited_by: { object: "user", id: string }, cover: { type: "external" | "file", external?: { url: string }, file?: { url: string, expiry_time: string } } | null, icon: { type: "emoji" | "external" | "file", emoji?: string, external?: { url: string }, file?: { url: string, expiry_time: string } } | null, archived: boolean, url: string }

Error Responses:

// 401 - Unauthorized (from auth middleware) { error: "Authentication required" } // 400 - Invalid Request { error: "Page ID is required", details?: string } // 500 - Server Error { error: "Failed to fetch page data", details: "Notion API error message" }

Data Filtering

View routes apply business logic filtering:

Button Property Removal

  • Purpose: Remove UI-specific properties that shouldn't be displayed in data views
  • Implementation: Controller filters out all properties where type === "button"
  • Reason: Button properties are interactive elements in Notion, not data to be displayed

Property Type Examples

// Filtered OUT (not included in response) { "Action Button": { "type": "button", "button": {} } } // Included in response { "Title": { "type": "title", "title": [ { "type": "text", "text": { "content": "Page Title" }, "plain_text": "Page Title" } ] }, "Status": { "type": "select", "select": { "id": "abc123", "name": "Active", "color": "green" } }, "URL": { "type": "url", "url": "https://example.com" } }

Authentication Flow

  1. Request arrives at /views/glimpse/:id
  2. Auth middleware validates Google OAuth token
  3. User context is added to request (email, etc.)
  4. Route handler extracts page ID and calls controller
  5. Controller applies business logic and filtering
  6. Response returns filtered page data

Usage Examples

# Authenticated request (requires valid session) curl -H "Cookie: session=..." https://your-val.web.val.run/views/glimpse/abc123 # Response includes filtered properties { "id": "abc123", "properties": { "Title": { "type": "title", "title": [...] }, "Status": { "type": "select", "select": {...} } // Button properties excluded } }

Comparison with API Routes

Aspect/views/glimpse/:id/api/demo/:id/properties
AuthenticationRequiredNot required
Data filteringButton properties removedButton properties removed
Use caseAuthenticated data accessPublic demo access
Response formatSame filtered structureSame filtered structure

Both endpoints use the same controller (getDemoProperties) but have different authentication requirements.