This is a full-stack application built with modern technologies for collecting, storing, and viewing GitHub starred repositories.
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ GitHub API │◄──────┤ Backend (Go) │◄──────┤ Frontend (Next)│
│ │ │ PocketBase │ │ React + TS │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│
▼
┌──────────────┐
│ SQLite DB │
│ (PocketBase)│
└──────────────┘
- PocketBase: Go-based backend-as-a-service with built-in database
- GitHub API Client: Custom implementation for fetching starred repos
- Cron Scheduler: Built-in job scheduling for automated collection
- RESTful API: Custom endpoints extending PocketBase
- Next.js 14: React framework with App Router
- TypeScript: Full type safety
- shadcn-ui: Pre-built accessible UI components
- Tailwind CSS: Utility-first styling
- Axios: HTTP client for API calls
{
id: string, // PocketBase record ID
github_user: string, // GitHub username
repo_id: number, // GitHub repository ID
repo_name: string, // Repository name
full_name: string, // owner/repo format
description: string, // Repository description
html_url: string, // Repository URL
star_num: number, // Star count
language: string, // Primary language
fork_num: number, // Fork count
tags: string, // Comma-separated topics
created_at: timestamp, // Repo creation date
updated_at: timestamp, // Last update date
pushed_at: timestamp, // Last push date
collected_at: timestamp // Data collection date
}{
id: string, // PocketBase record ID
github_user: string, // GitHub username
enabled: boolean, // Scheduled collection enabled
schedule: string, // Cron schedule
last_collected: timestamp // Last collection time
}Endpoint: GET /api/github/starred/:username
Implementation:
- Uses GitHub REST API v3
- Implements pagination (100 items per page)
- Supports authentication via GITHUB_TOKEN env var
- Returns all starred repos for a user
Code Flow:
func fetchGitHubStarredRepos(username string) ([]GitHubRepo, error) {
// Paginate through all starred repos
// Build request with proper headers
// Handle rate limiting
// Return all repos
}Endpoint: POST /api/github/collect/:username
Implementation:
- Fetches repos from GitHub
- Transforms data to match database schema
- Upserts records (avoids duplicates)
- Returns collection statistics
Key Features:
- Batch insert for performance
- Automatic tag conversion (array to comma-separated string)
- Timestamp conversion (GitHub API to PocketBase format)
- Error handling for partial failures
Endpoint: GET /api/starred/search
Implementation:
- Uses PocketBase's filter expressions
- Supports multiple filter criteria:
min_stars,max_stars: Numeric range filterlanguage: Exact matchtag: Partial match using~operator
- Implements pagination
- Sorts by star count (descending)
Filter Expression Example:
expr := fmt.Sprintf("github_user = '%s'", username)
if minStars != "" {
expr += fmt.Sprintf(" && star_num >= %s", minStars)
}
// Additional filters...Endpoint: GET /api/starred/languages/:username
Implementation:
- Fetches all repos for user
- Aggregates language counts
- Returns as key-value map
Endpoint: GET /api/starred/tags/:username
Implementation:
- Parses comma-separated tags
- Counts tag occurrences
- Returns as key-value map
-
User Input Section
- Username input field
- Collect repos button
- Real-time search
-
Filter Section
- Star range inputs (min/max)
- Language dropdown (dynamically populated)
- Tag dropdown (dynamically populated)
- Search button
-
Results Grid
- Card-based layout (responsive grid)
- Each card shows:
- Repository name
- Description
- Star/Fork counts
- Language badge
- Tags
- External link
-
Pagination
- Previous/Next buttons
- Page indicator
const [username, setUsername] = useState('qdriven')
const [repos, setRepos] = useState<StarredRepo[]>([])
const [languages, setLanguages] = useState<Record<string, number>>({})
const [tags, setTags] = useState<Record<string, number>>({})
const [loading, setLoading] = useState(false)
const [collecting, setCollecting] = useState(false)
const [selectedLanguage, setSelectedLanguage] = useState<string>('')
const [selectedTag, setSelectedTag] = useState<string>('')
const [minStars, setMinStars] = useState<string>('')
const [maxStars, setMaxStars] = useState<string>('')export const api = {
async fetchStarredRepos(username: string),
async collectStarredRepos(username: string),
async searchStarredRepos(params: SearchParams),
async getLanguages(username: string),
async getTags(username: string),
}scheduler := cron.New()
scheduler.MustAdd("collect_starred_repos", "0 0 * * *", func() {
// 1. Query collection_configs collection
// 2. For each enabled config:
// - Fetch starred repos
// - Save to database
// - Update last_collected timestamp
})
scheduler.Start()Triggers:
- Scheduled: Daily at midnight UTC
- Manual: workflow_dispatch
Steps:
- Checkout repository
- Set up Go environment
- Build backend
- Start PocketBase server
- Run collection script
- Export data to JSON
- Commit changes to repository
-
Backend Build:
cd backend go mod download go build -o github-collector .
-
Frontend Build:
cd frontend npm install npm run build
Backend Dockerfile:
- Multi-stage build (builder + production)
- Alpine-based for minimal size
- Includes migrations
- Exposes port 8090
Frontend Dockerfile:
- Multi-stage build
- Standalone output mode
- Static file serving
- Exposes port 3000
version: '3.8'
services:
backend:
build:
context: .
dockerfile: Dockerfile.backend
ports:
- "8090:8090"
environment:
- GITHUB_TOKEN=${GITHUB_TOKEN}
volumes:
- ./backend/pb_data:/app/pb_data
frontend:
build:
context: .
dockerfile: Dockerfile.frontend
ports:
- "3000:3000"
environment:
- NEXT_PUBLIC_API_URL=http://localhost:8090
depends_on:
- backend-
API Security:
- Currently no authentication (can be added)
- Rate limiting via GitHub token
- Input validation
-
Data Security:
- SQLite database (file-based)
- No sensitive data stored
- Regular backups recommended
-
Environment Variables:
- GITHUB_TOKEN for API access
- NEXT_PUBLIC_API_URL for frontend
- Never commit secrets to repository
-
Backend:
- Batch inserts for collection
- Indexed fields (github_user, repo_id, star_num, language)
- Pagination for search results
- Efficient filter expressions
-
Frontend:
- Lazy loading components
- Memoized callbacks
- Optimized re-renders
- Responsive grid layout
-
API:
- Connection pooling
- Request caching
- Pagination support
cd backend
go test -v ./...cd frontend
npm run lint # ESLint
npm run type-check # TypeScript
npm test # Jest (if configured)app.Logger().Info("Collecting starred repos", "username", username)
app.Logger().Error("Failed to fetch repos", "error", err)- Console logging for development
- Error boundary for production
- Network error handling
- Authentication: Add user authentication for private collections
- Real-time Updates: WebSocket support for live updates
- Analytics: Track popular repositories, trends
- Export Options: CSV, JSON export functionality
- Comparison: Compare starred repos between users
- Recommendations: ML-based repository recommendations
- Mobile App: React Native mobile application
- GraphQL: Alternative API with GraphQL
-
GitHub API Rate Limits:
- 60 requests/hour without token
- 5000 requests/hour with token
-
Pagination:
- Currently fetches all pages
- May be slow for users with many starred repos
-
Search Performance:
- SQLite may be slow with large datasets
- Consider PostgreSQL for production
-
No Real-time Updates:
- Requires manual refresh
- Scheduled updates only
-
PocketBase won't start:
- Check if port 8090 is available
- Verify pb_data directory permissions
-
GitHub API errors:
- Verify GITHUB_TOKEN is set
- Check rate limit status
- Validate username exists
-
Frontend can't connect:
- Verify NEXT_PUBLIC_API_URL
- Check CORS settings
- Ensure backend is running
-
Database errors:
- Check pb_data directory
- Verify migrations ran
- Check database schema
This implementation provides a robust, scalable solution for collecting and viewing GitHub starred repositories. The architecture separates concerns cleanly, uses modern best practices, and can be extended for additional features.
For API usage examples and detailed endpoint documentation, see API.md.