Here are the step-by-step instructions that would lead directly to the correct, clean implementation:
- 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
cat /backend/services/notion.service.ts
cat /backend/routes/views/\_views.routes.ts
cat /main.tsx # Look for auth middleware setup
- Current service has getDatabaseById() for database metadata
- Need getPageById() for individual page data
- Service should return full Notion response for flexibility
// 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(),
};
}
}
// 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);
}
// 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;
Add to /backend/routes/views/README.md
- 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
✅ 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
{ "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" }
✅ 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
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"
- 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
✅ 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.