Polling Data API
**Referenced Files in This Document** - [worker.js](file://worker.js) - [polling.js](file://src/_data/polling.js) - [polling-bar.njk](file://src/_includes/macros/polling-bar.njk) - [poll-bar-animation.js](file://src/assets/js/modules/poll-bar-animation.js) - [polling.njk](file://src/services/polling.njk) - [netlify.toml](file://netlify.toml) - [cloudflare-pages.toml](file://cloudflare-pages.toml) - [wrangler.jsonc](file://wrangler.jsonc) - [package.json](file://package.json) - [README.md](file://README.md)Table of Contents
- Introduction
- Project Structure
- Core Components
- Architecture Overview
- Detailed Component Analysis
- Dependency Analysis
- Performance Considerations
- Troubleshooting Guide
- Conclusion
- Appendices
Introduction
This document provides comprehensive API documentation for the live polling data endpoint that serves real-time polling information from Google Sheets. It covers the GET /api/polling.json endpoint, including query parameters, response data structure, caching strategy, Google Sheets integration, error handling, and client consumption patterns. It also documents fallback behavior and the automatic data refresh mechanism.
Project Structure
The polling data API is implemented in a Cloudflare Worker and integrates with Eleventy-generated static content. The endpoint is routed through the Worker and backed by Google Sheets data. Frontend components consume the endpoint to render polling visuals.
graph TB
subgraph "Client"
Browser["Browser"]
end
subgraph "Cloudflare Worker"
FetchHandler["Main fetch handler"]
PollingRoute["Route: GET /api/polling.json"]
GoogleSheets["Google Sheets API"]
end
subgraph "Static Site (Eleventy)"
Macros["polling-bar.njk macro"]
Animation["poll-bar-animation.js"]
Page["polling.njk page"]
end
Browser --> FetchHandler
FetchHandler --> PollingRoute
PollingRoute --> GoogleSheets
Browser --> Macros
Macros --> Animation
Page --> Macros
Diagram sources
- [worker.js:301-321](file://worker.js#L301-L321)
- [worker.js:233-276](file://worker.js#L233-L276)
- [polling-bar.njk:1-39](file://src/_includes/macros/polling-bar.njk#L1-L39)
- [poll-bar-animation.js:1-17](file://src/assets/js/modules/poll-bar-animation.js#L1-L17)
- [polling.njk:1-90](file://src/services/polling.njk#L1-L90)
Section sources
- [worker.js:301-321](file://worker.js#L301-L321)
- [polling-bar.njk:1-39](file://src/_includes/macros/polling-bar.njk#L1-L39)
- [poll-bar-animation.js:1-17](file://src/assets/js/modules/poll-bar-animation.js#L1-L17)
- [polling.njk:1-90](file://src/services/polling.njk#L1-L90)
Core Components
- Cloudflare Worker route handler for GET /api/polling.json
- Google Sheets integration using document ID and API key
- Caching headers with 5-minute freshness and 1-hour stale-while-revalidate
- Fallback values when data is unavailable
- Frontend macro and animation for rendering polling visuals
Section sources
- [worker.js:233-276](file://worker.js#L233-L276)
- [polling.js:1-17](file://src/_data/polling.js#L1-L17)
- [polling-bar.njk:1-39](file://src/_includes/macros/polling-bar.njk#L1-L39)
- [poll-bar-animation.js:1-17](file://src/assets/js/modules/poll-bar-animation.js#L1-L17)
Architecture Overview
The polling API is served by a Cloudflare Worker that validates environment variables, queries Google Sheets for the requested state, transforms the response, and returns JSON with appropriate caching headers. The static site renders the data using Nunjucks macros and JavaScript animations.
sequenceDiagram
participant Client as "Client"
participant Worker as "Worker fetch handler"
participant Route as "handlePollingApi"
participant Sheets as "Google Sheets API"
Client->>Worker : "GET /api/polling.json?state=sa|federal"
Worker->>Route : "Dispatch to polling route"
Route->>Route : "Validate GOOGLE_SHEETS_ID and GOOGLE_SHEETS_API_KEY"
alt "Misconfigured"
Route-->>Client : "503 Service Misconfigured"
else "Configured"
Route->>Sheets : "Fetch range based on state"
Sheets-->>Route : "JSON values"
Route->>Route : "Parse values and construct response"
Route-->>Client : "200 JSON with cache headers"
end
Diagram sources
- [worker.js:301-321](file://worker.js#L301-L321)
- [worker.js:233-276](file://worker.js#L233-L276)
Section sources
- [worker.js:233-276](file://worker.js#L233-L276)
- [worker.js:301-321](file://worker.js#L301-L321)
Detailed Component Analysis
Endpoint Definition
- Path: /api/polling.json
- Method: GET
- Purpose: Serve live polling data for South Australia or Federal elections from Google Sheets
- CORS: Access-Control-Allow-Origin set to https://acestrategies.au
Section sources
- [worker.js:233-276](file://worker.js#L233-L276)
Query Parameters
- state (optional): One of sa or federal
- sa: Uses range "SA Polling!A2:H2"
- federal: Uses range "Federal Polling!A2:H2"
- Default behavior when omitted is to return South Australia data
Section sources
- [worker.js:249-249](file://worker.js#L249-L249)
Response Data Structure
The endpoint returns a JSON object containing:
- state: String indicating the selected state ("sa" or "federal")
- labor_tpp: Number representing Labor's Two-Party Preferred percentage
- liberal_tpp: Number representing Liberal's Two-Party Preferred percentage
- labor_primary: Number representing Labor's primary vote percentage
- liberal_primary: Number representing Liberal's primary vote percentage
- greens_primary: Number representing Greens' primary vote percentage
- sample_size: Integer representing the sample size
- margin_of_error: Number representing the margin of error
- last_updated: String date in YYYY-MM-DD format
- _cached_at: String timestamp indicating when the response was generated
Fallback values are applied when individual fields are missing or invalid:
- labor_tpp defaults to 66
- liberal_tpp defaults to 34
- labor_primary defaults to 47
- liberal_primary defaults to 21
- greens_primary defaults to 13
- sample_size defaults to 1006
- margin_of_error defaults to 3.9
Section sources
- [worker.js:256-267](file://worker.js#L256-L267)
- [polling.js:3-15](file://src/_data/polling.js#L3-L15)
Caching Strategy
- Cache-Control: public, max-age=300, stale-while-revalidate=3600
- Freshness: 5 minutes
- Stale-while-revalidate: 1 hour
- Origin: https://acestrategies.au
Section sources
- [worker.js:234-238](file://worker.js#L234-L238)
Google Sheets Integration
- Environment variables:
- GOOGLE_SHEETS_ID: The Google Sheets document ID
- GOOGLE_SHEETS_API_KEY: The Sheets API read key
- Request construction:
- Base URL: https://sheets.googleapis.com/v4/spreadsheets/{sheetId}/values/{range}?key={apiKey}
- Range selection depends on state query parameter
- Data parsing:
- Reads the first row of the selected range
- Converts values to appropriate types (floats/integers) with fallbacks
Section sources
- [worker.js:240-252](file://worker.js#L240-L252)
- [worker.js:253-254](file://worker.js#L253-L254)
- [README.md:507-508](file://README.md#L507-L508)
Data Transformation Logic
- State normalization: Maps "federal" to "federal", otherwise "sa"
- Value parsing:
- Numeric fields parsed with parseFloat or parseInt
- Fallbacks applied when values are missing or NaN
- Timestamps:
- last_updated: Falls back to current date in YYYY-MM-DD format
- _cached_at: ISO timestamp of response generation
Section sources
- [worker.js:256-267](file://worker.js#L256-L267)
Error Handling
- Service misconfiguration:
- Missing GOOGLE_SHEETS_ID or GOOGLE_SHEETS_API_KEY
- Returns 503 with error message
- Google Sheets API errors:
- Catch-all exception handling
- Returns 500 with error message
- CORS:
- Access-Control-Allow-Origin header set to https://acestrategies.au
Section sources
- [worker.js:244-246](file://worker.js#L244-L246)
- [worker.js:270-275](file://worker.js#L270-L275)
Client Consumption Patterns
- Static site integration:
- The polling macro renders the data with fallback badges and statistics
- JavaScript animation triggers width transitions when elements intersect
- Example usage in templates:
- The macro accepts data and optional flags for fallback rendering
- Provides default values for missing fields
Section sources
- [polling-bar.njk:1-39](file://src/_includes/macros/polling-bar.njk#L1-L39)
- [poll-bar-animation.js:1-17](file://src/assets/js/modules/poll-bar-animation.js#L1-L17)
- [polling.njk:1-90](file://src/services/polling.njk#L1-L90)
Automatic Data Refresh Mechanism
- The Worker sets Cache-Control headers enabling client-side caching with revalidation
- Clients should respect cache headers and revalidate after 5 minutes
- Stale responses are allowed for up to 1 hour while revalidation occurs
Section sources
- [worker.js:234-238](file://worker.js#L234-L238)
Dependency Analysis
The polling API depends on:
- Cloudflare Worker runtime and environment bindings
- Google Sheets API for data retrieval
- Static site rendering pipeline for frontend consumption
graph LR
Worker["worker.js"] --> Env["Environment Variables"]
Worker --> Sheets["Google Sheets API"]
Worker --> Client["Browser"]
Client --> Macro["polling-bar.njk"]
Macro --> Animation["poll-bar-animation.js"]
Diagram sources
- [worker.js:233-276](file://worker.js#L233-L276)
- [polling-bar.njk:1-39](file://src/_includes/macros/polling-bar.njk#L1-L39)
- [poll-bar-animation.js:1-17](file://src/assets/js/modules/poll-bar-animation.js#L1-L17)
Section sources
- [worker.js:233-276](file://worker.js#L233-L276)
- [polling-bar.njk:1-39](file://src/_includes/macros/polling-bar.njk#L1-L39)
- [poll-bar-animation.js:1-17](file://src/assets/js/modules/poll-bar-animation.js#L1-L17)
Performance Considerations
- Caching: 5-minute freshness reduces load on Google Sheets and improves response times
- Stale-while-revalidate: Allows continued serving of stale data while refreshing in the background
- Client-side animations: Trigger on intersection to optimize rendering performance
- Static asset caching: Separate caching policies apply to static assets
Section sources
- [worker.js:234-238](file://worker.js#L234-L238)
- [poll-bar-animation.js:1-17](file://src/assets/js/modules/poll-bar-animation.js#L1-L17)
- [netlify.toml:22-26](file://netlify.toml#L22-L26)
Troubleshooting Guide
Common issues and resolutions:
- 503 Service Misconfigured:
- Cause: Missing GOOGLE_SHEETS_ID or GOOGLE_SHEETS_API_KEY
- Resolution: Set required secrets via wrangler
- 500 Internal Server Error:
- Cause: Google Sheets API failure or network issues
- Resolution: Retry request; verify API key permissions and document accessibility
- CORS Issues:
- Cause: Origin not permitted
- Resolution: Ensure requests originate from https://acestrategies.au
- Missing Data Fields:
- Behavior: Fallback values are applied automatically
- Resolution: Verify Google Sheets ranges and column order
Section sources
- [worker.js:244-246](file://worker.js#L244-L246)
- [worker.js:270-275](file://worker.js#L270-L275)
- [worker.js:234-238](file://worker.js#L234-L238)
- [worker.js:256-267](file://worker.js#L256-L267)
Conclusion
The polling API provides a reliable, cached interface to live polling data from Google Sheets. It supports state filtering, robust error handling, and integrates seamlessly with the static site's rendering pipeline. Clients should respect the caching headers and implement appropriate fallbacks for resilient user experiences.
Appendices
API Definition
- Endpoint: GET /api/polling.json
- Query Parameters:
- state: sa or federal (default: sa)
- Response Fields:
- state, labor_tpp, liberal_tpp, labor_primary, liberal_primary, greens_primary, sample_size, margin_of_error, last_updated, _cached_at
- Cache-Control: public, max-age=300, stale-while-revalidate=3600
- CORS: Access-Control-Allow-Origin: https://acestrategies.au
Section sources
- [worker.js:233-276](file://worker.js#L233-L276)
- [worker.js:234-238](file://worker.js#L234-L238)
Environment Setup
Required secrets:
- GOOGLE_SHEETS_ID: Google Sheets document ID
- GOOGLE_SHEETS_API_KEY: Google Sheets API read key
Deployment and scripts:
- Deployment: npm run deploy (wrangler deploy)
- Preview: npm run preview (wrangler dev)
Section sources
- [README.md:507-508](file://README.md#L507-L508)
- [package.json:11-12](file://package.json#L11-L12)
- [package.json:12-12](file://package.json#L12-L12)
Frontend Integration Notes
- Macro usage: polling-bar.njk renders the polling visualization with fallback support
- Animation: poll-bar-animation.js triggers width transitions on intersection
- Page template: polling.njk integrates the macro into the services page
Section sources
- [polling-bar.njk:1-39](file://src/_includes/macros/polling-bar.njk#L1-L39)
- [poll-bar-animation.js:1-17](file://src/assets/js/modules/poll-bar-animation.js#L1-L17)
- [polling.njk:1-90](file://src/services/polling.njk#L1-L90)