A modern voting and feedback application built with OpenTelemetry observability, deployed on Cloudflare's edge infrastructure.
Ballot App is a real-time voting platform that allows users to create polls with color-coded feedback (green/yellow/red) and optional comments. Perfect for gathering team feedback, conducting surveys, or making collaborative decisions.
- Color-Coded Voting System: Green (positive), Yellow (neutral), Red (negative) voting options
- Real-Time Comments: Add optional detailed feedback with votes
- Ballot Management: Create, view, and track voting results
- Responsive Design: Clean, modern UI built with TailwindCSS and Radix UI
- Full Observability: OpenTelemetry instrumentation with Honeycomb integration
- Edge Deployment: Deployed on Cloudflare Workers/Pages for global performance
Frontend
- React 19 with TypeScript
- TailwindCSS for styling
- Radix UI components
- Vite for build tooling
- Deployed on Cloudflare Pages
Backend
- Hono web framework
- TypeScript for type safety
- OpenTelemetry for observability
- Deployed on Cloudflare Workers
Observability
- OpenTelemetry SDK with custom spans
- Honeycomb for telemetry data collection
- Custom metrics for voting patterns and API performance
.
βββ client/ # React frontend application
β βββ src/
β β βββ components/ # UI components (BallotList, BallotDetail, etc.)
β β βββ App.tsx # Main application component
β βββ package.json
βββ server/ # Hono backend API
β βββ src/
β β βββ index.ts # API routes with telemetry
β β βββ telemetry.ts # OpenTelemetry configuration
β βββ package.json
βββ shared/ # Shared TypeScript types
β βββ src/types/
βββ wrangler.toml # Cloudflare deployment configuration
Base URL: https://ballot-app-server.siener.workers.dev
GET /api/ballots- Retrieve all ballotsGET /api/ballots/:id- Get specific ballot detailsPOST /api/ballots- Create a new ballotPUT /api/ballots/:id- Add vote to existing ballot
Use these endpoints to programmatically create and manage attendance polls.
POST /api/attendance
Content-Type: application/json
{
"title": "Tuesday Practice β March 11, Team A @ Fieldhouse",
"date": "2025-03-11"
}
Returns the created attendance object (201):
{
"id": "attendance-1741...",
"title": "Tuesday Practice β March 11, Team A @ Fieldhouse",
"date": "2025-03-11",
"responses": [],
"createdAt": "2025-03-06T12:00:00.000Z",
"updatedAt": "2025-03-06T12:00:00.000Z",
"version": 1
}The shareable poll URL is: https://ballot.io/attendance/{id}
Tip: Encode date, time, location, and team into the title string β it's free-form text up to 200 characters.
GET /api/attendance/{id}
Returns the attendance object with all responses:
{
"id": "attendance-1741...",
"title": "Tuesday Practice β March 11, Team A @ Fieldhouse",
"date": "2025-03-11",
"responses": [
{ "name": "Alice", "attending": true, "timestamp": "2025-03-06T14:00:00Z" },
{ "name": "Bob", "attending": false, "timestamp": "2025-03-06T14:05:00Z" }
],
"version": 3
}To get counts, compute from the responses array:
- yes:
responses.filter(r => r.attending === true).length - no:
responses.filter(r => r.attending === false).length
GET /api/attendance
Returns all attendance polls sorted by date (most recent first). Filter client-side for active/upcoming polls by comparing date to today.
PUT /api/attendance/{id}
Content-Type: application/json
{
"name": "Alice",
"attending": true
}
If the same name responds again, their previous response is updated.
GET /api/attendance/batch?ids=id1,id2,id3
Returns multiple attendance polls in one request.
- Bun runtime
- Wrangler CLI for deployment
- Honeycomb account for observability (optional)
# Clone the repository
git clone https://github.com/gsiener/ballot-app.git
cd ballot-app
# Install dependencies
bun install# Run all services in development mode
bun run dev
# Or run individually
bun run dev:client # Frontend dev server
bun run dev:server # Backend API server
bun run dev:shared # Watch shared types# Build everything
bun run build
# Or build individually
bun run build:client
bun run build:server
bun run build:sharedSet your Honeycomb API key for observability:
# Set Honeycomb API key as Cloudflare secret
npx wrangler secret put HONEYCOMB_API_KEY# Deploy backend (Workers)
npx wrangler deploy
# Deploy frontend (Pages)
npx wrangler pages deploy client/dist --project-name ballot-app-clientThe application includes comprehensive OpenTelemetry instrumentation:
- API request/response times
- Ballot creation and voting patterns
- Error rates and debugging information
- Custom business events (ballot_created, vote_added, etc.)
- Real-time performance monitoring
- Distributed tracing across API calls
- Custom dashboards for voting analytics
- Error tracking and alerting
- Create a Ballot: Enter a question to gather feedback on
- Share the Link: Send the ballot URL to participants
- Vote: Users select green (positive), yellow (neutral), or red (negative)
- Add Comments: Optional detailed feedback with votes
- View Results: Real-time vote counts and comment threads
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests if applicable
- Submit a pull request
MIT License - see LICENSE file for details