A modern web application that displays the number of players drafted by each NBA team in the first two rounds. Built with React, TypeScript, and deployed on Cloudflare Workers.
Live Demo: nba-draft-tracker.jasonbland.dev
- Overview
- Architecture
- Tech Stack
- Getting Started
- Project Structure
- How It Works
- Development
- Testing
- Deployment
- Configuration
- Roadmap
- Known Limitations
The NBA Draft Tracker provides an easy way to view draft analytics for NBA teams. Users can select any team from a dropdown and see how many players were drafted in rounds 1 and 2, helping visualize team-building strategies through the draft.
Key Features:
- All 30 NBA teams available for selection
- Real-time data from Ball Don't Lie API
- Draft round breakdown (rounds 1-2)
- Responsive UI with React Bootstrap
- Fast edge deployment via Cloudflare Workers
The application follows a three-tier architecture with clear separation of concerns:
┌─────────────────┐
│ React UI │ ← Presentational components (SelectTeamDropdown, DraftRoundCount)
└────────┬────────┘
│
┌────────▼────────┐
│ State & Logic │ ← App.tsx (orchestration, state management)
└────────┬────────┘
│
┌────────▼────────┐
│ API Layer │ ← balldontlieApi.ts (SDK wrapper, data transformation)
└────────┬────────┘
│
┌────────▼────────┐
│ Ball Don't Lie │ ← External NBA stats API
│ API │
└─────────────────┘
Design Patterns:
- Component Composition: Presentational components receive data via props
- Container/Presenter: App.tsx acts as container component managing state
- API Abstraction: balldontlieApi.ts provides clean interface to external SDK
- Error Boundaries: Graceful degradation with user-friendly error messages
- Edge-First: Cloudflare Workers for low-latency global distribution
- React 19 - UI library with hooks
- TypeScript 5.8 - Type safety and developer experience
- Vite 6.3 - Fast build tool with HMR
- React Bootstrap 2.10 - Responsive component library
- Bootstrap 5.3 - CSS framework
- Cloudflare Workers - Serverless edge compute platform
- Hono 4.7 - Lightweight web framework for Workers
- Wrangler 4.12 - Cloudflare developer tooling
- Ball Don't Lie API - NBA player and team data via @balldontlie/sdk
- Jest 29.7 - Testing framework
- React Testing Library 16.3 - Component testing utilities
- ESLint 9.25 - Code quality and style enforcement
- ts-jest - TypeScript transformation for Jest
- Node.js 18+ and npm
- (Optional) Cloudflare account for deployment
# Clone the repository
git clone https://github.com/jasonbland/nba-draft-tracker.git
cd nba-draft-tracker
# Install dependencies
npm install# Start Vite dev server
npm run devYour application will be available at http://localhost:5173
nba-draft-tracker/
├── src/
│ ├── app/ # React application
│ │ ├── App.tsx # Main component (state management)
│ │ ├── main.tsx # React DOM entry point
│ │ ├── App.css # Application styles
│ │ └── index.css # Global styles
│ ├── components/ # React UI components
│ │ ├── SelectTeamDropdown.tsx # Team selection dropdown
│ │ ├── NBATeamsForm.tsx # Form wrapper component
│ │ ├── DraftRoundCount.tsx # Results display
│ │ └── ErrorPage.tsx # Error UI
│ ├── functions/
│ │ └── balldontlieApi.ts # API integration layer
│ └── worker/
│ └── index.ts # Cloudflare Worker entry point
├── test/
│ ├── app/
│ │ └── App.test.tsx # Integration tests
│ ├── mocks/
│ │ └── fileMock.js # Asset mocking for Jest
│ └── jest.setup.ts # Jest configuration
├── public/ # Static assets
├── dist/ # Build output (gitignored)
├── package.json
├── tsconfig.json # Root TypeScript config
├── tsconfig.app.json # App-specific TS config
├── tsconfig.worker.json # Worker-specific TS config
├── vite.config.ts # Vite build configuration
├── jest.config.ts # Jest test configuration
├── wrangler.json # Cloudflare Worker config
└── eslint.config.js # ESLint rules
src/app/- React application entry point and main componentsrc/components/- Reusable React components (presentation layer)src/functions/- Business logic and API integrationsrc/worker/- Cloudflare Workers edge runtime codetest/- Test files and test configuration
-
Initialization
- App.tsx mounts and triggers useEffect hook
getNBATeams()fetches all 30 NBA teams from Ball Don't Lie API- Teams populate SelectTeamDropdown component
-
Team Selection
- User selects a team from dropdown
- Form submission triggers state update with selected team ID
-
Player Lookup
getNBAPlayers({ teamId })fetches all players for the selected team- Returns array of NBAPlayer objects with draft_round property
-
Draft Analysis
getRoundCountForSelectedTeam({ teamPlayers })processes player data- Filters players by draft_round (rounds 1-2 only)
- Aggregates count per round into RoundCounts object
-
Results Display
- DraftRoundCount component receives round counts as props
- Displays team name and draft distribution
- Updates UI reactively as selections change
The balldontlieApi.ts module provides three key functions:
// Fetch all NBA teams
getNBATeams(): Promise<NBATeam[]>
// Fetch players for a specific team
getNBAPlayers({ teamId }): Promise<NBAPlayer[]>
// Calculate draft round distribution
getRoundCountForSelectedTeam({ teamPlayers }): RoundCountsAll API calls include error handling that gracefully degrades to empty arrays on failure.
# Start development server with hot reload
npm run dev
# Run type checking
tsc -b
# Lint code
npm run lint
# Run all tests
npm run test
# Build for production
npm run build
# Preview production build locally
npm run preview
# Validate build (type check + build + dry-run deploy)
npm run checkThe project enforces code quality through:
- TypeScript strict mode - Compile-time type safety
- ESLint - Linting with React hooks rules
- React Refresh - Fast refresh during development
- Create component in
src/components/(presentation) - Add business logic to
src/functions/(data processing) - Update App.tsx for state orchestration
- Write tests in
test/app/ - Run
npm run checkto validate
- Jest 29.7 with jsdom environment
- React Testing Library 16.3 for component testing
- ts-jest for TypeScript transformation
# Run all tests
npm run test
# Run tests in watch mode
npm run test -- --watch
# Run with coverage
npm run test -- --coverageCurrent test suite includes:
- Teams load and populate dropdown
- Error handling on initial API failure
- Full user flow (team selection → draft round display)
- Error handling on player lookup failure
test/
├── app/
│ └── App.test.tsx # Integration tests for main component
├── mocks/
│ └── fileMock.js # Mock handlers for static assets
└── jest.setup.ts # Global test setup
- API functions (
balldontlieApi.ts) are mocked using Jest manual mocks - Static assets (CSS, images) are stubbed with fileMock.js
- React Testing Library provides userEvent simulation
The application deploys to Cloudflare Workers, a serverless edge platform that runs code globally.
# Build the application
npm run build
# Deploy to Cloudflare Workers
npm run deploy
# Or combine both steps
npm run build && npm run deployDeployment settings are in wrangler.json:
{
"name": "nba-draft-tracker",
"main": "./src/worker/index.ts",
"compatibility_date": "2025-04-01",
"compatibility_flags": ["nodejs_compat"],
"observability": { "enabled": true },
"upload_source_maps": true,
"assets": {
"not_found_handling": "single-page-application"
}
}Key Settings:
- SPA mode - Routes 404s to index.html for client-side routing
- Node.js compatibility - Enables Node.js APIs in Workers runtime
- Observability - Telemetry and logging enabled
- Source maps - Uploaded for better error debugging
The project includes a check script for validation:
npm run check # TypeScript check + build + dry-run deployThis can be integrated into CI pipelines to validate PRs before merge.
The project uses composite TypeScript configurations:
tsconfig.json- Root configuration with project referencestsconfig.app.json- Frontend app (ES2020, DOM libs)tsconfig.worker.json- Cloudflare Worker (ES2022, Worker types)tsconfig.node.json- Build tools (ES2023)
Strict mode is enabled across all configurations.
Currently the Ball Don't Lie API key is hardcoded in src/functions/balldontlieApi.ts:4.
Future improvement: Migrate to environment variable:
const API_KEY = process.env.BALLDONTLIE_API_KEY;Then configure in Cloudflare Workers dashboard or wrangler.toml:
[vars]
BALLDONTLIE_API_KEY = "your-api-key"vite.config.ts includes:
- React plugin for JSX transformation
- Cloudflare plugin for Workers integration
- Build optimizations for production
Based on TODO comments and potential enhancements:
- Environment variable for API key - Remove hardcoded key from source
- Team filtering - Option to filter inactive teams from dropdown
- Client-side caching - Cache team data in localStorage to reduce API calls
- Loading states - Add loading indicator during player lookups
- Observability integration - Log errors to Splunk or similar platform
- Extended draft rounds - Support rounds 3-7 or all rounds
- Draft year filtering - Filter players by specific draft years
- Player details - Show individual player names and draft positions
- Pagination - Handle large datasets more efficiently
- Historical trends - Chart draft patterns over multiple years
- Team comparisons - Compare draft strategies across teams
- Draft value analysis - Calculate draft capital and value metrics
- Mobile app - React Native or PWA for mobile experience
- API endpoint - Expose draft analytics as public API
The Ball Don't Lie API has rate limits that may affect heavy usage. Error handling displays a generic message when rate limits are hit.
Mitigation strategies:
- Implement client-side caching for team data
- Add request throttling/debouncing
- Consider API key rotation or paid tier
Player data is fetched on-demand but not automatically refreshed. Users must reload to get updated rosters.
Potential solutions:
- Periodic background refresh
- Cache invalidation strategies
- WebSocket or Server-Sent Events for real-time updates
All API errors currently show the same generic error page. Different error types (rate limit, network, 404) are not distinguished.
Improvements:
- Differentiate error types with specific messages
- Retry logic for transient failures
- Graceful degradation with stale data
The application uses modern JavaScript (ES2020+) and may not support older browsers.
Minimum supported versions:
- Chrome 90+
- Firefox 88+
- Safari 14+
- Edge 90+
Contributions are welcome! Please follow these guidelines:
- Fork the repository and create a feature branch
- Follow existing patterns - Match code style and architecture
- Write tests - All new features should include test coverage
- Run validation -
npm run checkmust pass - Conventional commits - Use conventional commit format:
feat:for new featuresfix:for bug fixesdocs:for documentationtest:for test additionsrefactor:for code improvements
# Create feature branch
git checkout -b feat/your-feature-name
# Make changes and test
npm run test
npm run lint
npm run check
# Commit with conventional format
git commit -m "feat: add draft year filtering"
# Push and create PR
git push origin feat/your-feature-nameThis project is open source and available under the MIT License.
- Ball Don't Lie API - NBA data provider
- Cloudflare Workers - Edge compute platform
- React Team - UI framework
- Vite Team - Build tooling
Please open an issue on GitHub for bugs, feature requests, or questions.