A Node.js/TypeScript API that automatically tracks your food spending through multiple data sources: Uber CSV imports, financial aggregators (Plaid/TrueLayer), and email parsing as a fallback.
- Database Setup - PostgreSQL integration with Docker
- Receipt Management - Complete CRUD operations
- CSV/ZIP Import - Parse and import Uber Eats data
- Spending Analytics - Comprehensive user summaries and insights
- Wrapped Analytics - Spotify-style shareable insights (13 viral categories)
- Email Integration - Parse receipts from Gmail (now with OAuth connection!)
- Restaurant Chain Consolidation - Smart grouping across locations
- JWT Authentication - Secure user authentication and authorization
- Password Reset - OTP-based password recovery with email delivery
- Email Verification - Secure email verification with 6-digit OTP codes
- Rate Limiting - Viral-app-friendly per-user limits
- API Documentation - Complete Swagger/OpenAPI docs
- CI/CD Pipeline - Automated testing with GitHub Actions
- Authentication System - JWT with bcrypt password hashing
- ZIP File Upload - Auto-extract Uber data exports
- Route Protection - All endpoints secured with ownership validation
- Improved Rate Limiting - Per-user limits for viral growth
- Sentry Integration - Error tracking and monitoring (free tier ready)
- Configuration Cleanup - Removed hardcoded values
- Health Check Endpoint - Database connectivity monitoring
- Database Optimization - Indexes and connection pooling (20 connections prod, 10 dev)
- Redis Caching - Cache user summaries (5min TTL, 36.8% faster)
- Cache Invalidation - Automatic on data changes
- Graceful Shutdown - Proper connection cleanup
- Health Checks - Database latency monitoring
- Load Testing - ✅ Validated 1000+ concurrent users (production: p95: 844ms, p99: 908ms)
- Shame Analytics - 3am orders, lazy days, order streaks, chain dependency
- Flex Analytics - Most expensive order, coffee addiction, night owl stats
- Comparative Analytics - Investment calculator, cost equivalents
- Pattern Analytics - Peak hours, weekend patterns
- API Integration - Optional
?includeWrapped=trueparameter (backward compatible)
- Centralized Logging - Winston logger with dynamic log levels (no restart required)
- Zoom Capability - Switch to debug mode via API for detailed troubleshooting
- In-Memory Log Buffer - Last 1000 logs queryable via API
- Better Stack Integration - Optional cloud aggregation for 30+ day retention
- Sentry Integration - Complete error tracking and APM with performance monitoring
- APM Integration - Application performance monitoring via Sentry
- Alerting - Error rate and performance alerts with health monitoring
- Database Optimizations - GIN index on JSONB items, year column for partitioning
- Load Balancing Support - Stateless design, shared Redis cache, graceful shutdown, health checks
- Pagination for receipts (Doesn't do anything right now, future feature) - Handle large datasets efficiently
Timeline: Phase 1 ✅ | Phase 2 ✅ | Wrapped Analytics ✅ | Phase 3 ✅ | Production Ready! 🚀
Load Tested: 100% success (production: 50 users/1000 req, spike: 1000 users)
Performance: p95: 844ms, p99: 908ms (production), throughput: 125 req/s, wrapped: <30ms
Features: 13 viral-worthy analytics (shame, flex, comparative, patterns)
Monitoring: Structured logging, Sentry APM, automated backups, alerting
Deployment: Validated for Railway/Render (500-1000+ users)
Next: Frontend integration & polish → Production launch
All examples below use http://localhost:3000 for local development.
- Docker and Docker Compose
- Git
git clone https://github.com/harry-david-brown/SnackTrackAPI
cd SnackTrackAPI
docker-compose up --build -dThis automatically sets up:
- Local PostgreSQL database (no Railway connection needed)
- Local Redis cache (no Railway connection needed)
- Server on
http://localhost:3000(development mode)
Note: The service runs completely locally by default. It will NOT connect to Railway production services unless you explicitly configure environment variables to do so.
curl http://localhost:3000/
# Should return: ALIVEThat's it! The API is now running on http://localhost:3000
curl -X POST http://localhost:3000/auth/register \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "password": "SecurePass123"}'Response:
{
"userId": "550e8400-e29b-41d4-a716-446655440000",
"accessToken": "eyJhbGciOiJIUzI1NiIs...",
"refreshToken": "eyJhbGciOiJIUzI1NiIs...",
"user": { "id": "...", "email": "user@example.com" }
}Save the accessToken and userId - you'll need them for authenticated requests.
# Upload Uber Eats CSV file
curl -X POST http://localhost:3000/csv/import \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-F "csvFile=@path/to/your/uber-data.csv" \
-F "userId=YOUR_USER_ID"# Get spending summary
curl http://localhost:3000/users/YOUR_USER_ID/totalSpent \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
# Get comprehensive analytics
curl http://localhost:3000/users/YOUR_USER_ID/summary \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
# Get wrapped analytics (viral insights)
curl "http://localhost:3000/users/YOUR_USER_ID/summary?includeWrapped=true" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"All user-specific endpoints require JWT authentication.
- Minimum 8 characters
- At least 1 uppercase letter
- At least 1 number
- Access Token: Expires in 15 minutes
- Refresh Token: Expires in 7 days
- Use
/auth/refreshto get new tokens before expiry
# 1. Register
curl -X POST http://localhost:3000/auth/register \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "password": "SecurePass123"}'
# 2. Login (if already registered)
curl -X POST http://localhost:3000/auth/login \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "password": "SecurePass123"}'
# 3. Use token in requests
curl -X GET http://localhost:3000/users/{userId}/totalSpent \
-H "Authorization: Bearer {accessToken}"
# 4. Refresh token when needed
curl -X POST http://localhost:3000/auth/refresh \
-H "Content-Type: application/json" \
-d '{"refreshToken": "YOUR_REFRESH_TOKEN"}'# Request reset code
curl -X POST http://localhost:3000/auth/password/reset/request \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com"}'
# Complete reset
curl -X POST http://localhost:3000/auth/password/reset/complete \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "code": "123456", "newPassword": "NewPass123"}'# Send verification code
curl -X POST http://localhost:3000/auth/email/verify/send \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com"}'
# Confirm verification
curl -X POST http://localhost:3000/auth/email/verify/confirm \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "code": "123456"}'Note: OTP codes expire in 15 minutes. Rate limiting applies.
Base URL: http://localhost:3000 (local) | https://snacktrackapi-production.up.railway.app (production)
POST /auth/register- Register new userPOST /auth/login- Login with email/passwordPOST /auth/refresh- Refresh access tokenPOST /auth/logout- Logout (invalidate refresh token)POST /auth/password/reset/request- Request password reset codePOST /auth/password/reset/verify- Verify reset codePOST /auth/password/reset/complete- Complete password resetPOST /auth/email/verify/send- Send email verification codePOST /auth/email/verify/confirm- Confirm email verification
POST /csv/import- Import Uber Eats CSV/ZIP file- Requires:
Authorization: Bearer {token} - Form data:
csvFile(file),userId(string)
- Requires:
Mobile OAuth Flow:
GET /gmail/auth-url- Get OAuth URL for mobile apps- Requires:
Authorization: Bearer {token} - Returns:
{"authUrl": string, "state": string}
- Requires:
POST /gmail/exchange-token- Exchange authorization code for tokens- Requires:
Authorization: Bearer {token} - Body:
{"code": string}- Authorization code from Google
- Requires:
Import & Management:
GET /gmail/status- Check Gmail connection status- Requires:
Authorization: Bearer {token} - Returns:
{"connected": boolean, "email": string}
- Requires:
POST /gmail/import- Import Uber Eats receipts from Gmail- Requires:
Authorization: Bearer {token} - Body:
{"replaceExisting": boolean}(optional)
- Requires:
POST /gmail/disconnect- Disconnect Gmail account- Requires:
Authorization: Bearer {token}
- Requires:
📱 Designed for React Native - See MOBILE_INTEGRATION_GUIDE.md
GET /users/:id/totalSpent- Get total spending for user- Requires:
Authorization: Bearer {token} - Users can only access their own data
- Requires:
GET /users/:id/summary- Comprehensive user analytics- Requires:
Authorization: Bearer {token} - Query params:
includeWrapped=true(optional, adds viral insights) - Users can only access their own data
- Requires:
GET /receipts?userId={userId}- Get receipts with pagination and filtering- Requires:
Authorization: Bearer {token} - Required:
userIdquery parameter (must match authenticated user) - Query params:
page,limit,startDate,endDate,restaurantName,minAmount,maxAmount - Users can only query their own receipts
- Requires:
GET /database/users- List all users with statistics- Requires:
X-API-Key: {apiKey}header or?apiKey={apiKey}query parameter
- Requires:
GET /database/stats- Database statistics and health- Requires:
X-API-Key: {apiKey}header or?apiKey={apiKey}query parameter
- Requires:
DELETE /database/users/:id- Delete user and all receipts- Requires:
X-API-Key: {apiKey}header or?apiKey={apiKey}query parameter
- Requires:
Public Endpoints (No Authentication):
GET /monitoring/health- System health with metricsGET /monitoring/alerts- Alert status
Admin Endpoints (API Key Required):
GET /monitoring/log-level- Get current log levelPOST /monitoring/log-level- Change log level dynamically- Requires:
X-API-Key: {apiKey}header or?apiKey={apiKey}query parameter
- Requires:
GET /monitoring/logs- Get recent logs from in-memory buffer- Requires:
X-API-Key: {apiKey}header or?apiKey={apiKey}query parameter
- Requires:
GET /monitoring/cache- Redis cache statistics- Requires:
X-API-Key: {apiKey}header or?apiKey={apiKey}query parameter
- Requires:
GET /monitoring/test-sentry- Test Sentry error capture- Requires:
X-API-Key: {apiKey}header or?apiKey={apiKey}query parameter
- Requires:
GET /monitoring/database-optimizations- Database optimization status- Requires:
X-API-Key: {apiKey}header or?apiKey={apiKey}query parameter
- Requires:
GET /monitoring/query-performance- Query performance analysis- Requires:
X-API-Key: {apiKey}header or?apiKey={apiKey}query parameter
- Requires:
GET /- Basic health check (returns "ALIVE")GET /health- Detailed health check with database status
- Swagger UI available at
/docs(when running locally)
- JWT Authentication: Required for all user-specific endpoints. Get token via
/auth/loginor/auth/register - API Key Authentication: Required for admin endpoints (database, monitoring). Set
API_KEYenvironment variable in production - Ownership Validation: Users can only access their own data (enforced automatically)
src/
├── config/ # Configuration (database, Redis, Sentry, etc.)
├── models/ # Data models and TypeScript interfaces
├── routes/ # API route handlers
├── services/ # Business logic
│ ├── core/ # Core services (cache, container)
│ ├── data/ # Database access (PostgresService, repositories)
│ ├── email/ # Email processing (Gmail, Outlook)
│ ├── import/ # CSV import and data source management
│ └── receipt/ # Receipt processing logic
└── middleware/ # Express middleware (auth, pagination, error handling)
src/index.ts- Application entry point, server setupsrc/services/data/PostgresService.ts- Database connection and queriessrc/services/core/CacheService.ts- Redis cachingsrc/middleware/auth.ts- JWT authentication middlewaresrc/routes/- All API endpoints organized by feature
# Start all services (PostgreSQL, Redis, API)
docker-compose up --build -d
# View logs
docker-compose logs -f snack-track-api
# Stop services
docker-compose down
# Rebuild after dependency changes
docker-compose up --build -d- Make changes to TypeScript files in
src/ - Server auto-restarts (hot reload enabled)
- Test endpoints with curl or Postman
- Check logs in terminal or Railway dashboard
# Run test suites
cd tests
./test-full-suite.sh # Complete test suite
./test-auth-comprehensive.sh # Authentication tests
./test-pagination.sh # Pagination tests
./test-load-balancing.sh # Load balancing tests# Compile TypeScript
npm run build
# Test production build locally
npm startRequired:
DATABASE_URL- PostgreSQL connection stringJWT_SECRET- Secret key for JWT token signing
Optional:
REDIS_URL- Redis connection string (enables caching)SENTRY_DSN- Sentry error tracking (production)LOG_LEVEL- Logging level (error,warn,info,debug)LOGTAIL_TOKEN- Better Stack/Logtail token (long-term log storage)PORT- Server port (default: 3000)NODE_ENV- Environment (developmentorproduction)
The docker-compose.yml file includes:
- PostgreSQL - Database service
- Redis - Cache service
- API - Main application service
All services are pre-configured and start automatically.
Local Development (Default):
- Running
docker-compose upuses local PostgreSQL and Redis containers - Perfect for development and testing
- No Railway connection needed
Railway Production (Automatic):
- When you push code to Railway, it automatically deploys
- Railway uses its own environment variables (set in Railway dashboard)
- No code changes needed - Railway handles production configuration automatically
Optional: Local Development with Railway Services If you want to run locally but connect to Railway's production database/Redis (not recommended for most cases):
- Create a
.envfile with Railway credentials - Comment out the
environment:section indocker-compose.yml(lines 11-13) - Restart:
docker-compose up --build -d snack-track-api
View Logs:
# Local development (replace with production URL for production)
# Requires API key for admin endpoints
curl -H "X-API-Key: YOUR_API_KEY" http://localhost:3000/monitoring/logs
# Filter by level
curl -H "X-API-Key: YOUR_API_KEY" "http://localhost:3000/monitoring/logs?level=error&limit=50"Change Log Level (No Restart Required):
# Check current level (public endpoint)
curl http://localhost:3000/monitoring/log-level
# Switch to debug mode (requires API key)
curl -X POST http://localhost:3000/monitoring/log-level \
-H "Content-Type: application/json" \
-H "X-API-Key: YOUR_API_KEY" \
-d '{"level": "debug"}'Note: For production, replace http://localhost:3000 with your production URL.
Log Levels:
error- Errors onlywarn- Warnings and errorsinfo- Normal operation (default)debug- Full details with stack traces
Setup:
- Create account at sentry.io
- Create Node.js/Express project
- Copy DSN
- Set
SENTRY_DSNenvironment variable - Deploy - errors automatically tracked
Features:
- Automatic error capture (5xx errors)
- Performance monitoring (APM)
- User context tracking
- Breadcrumbs for debugging
# System health (public endpoint)
GET /monitoring/health
# Alert status (public endpoint)
GET /monitoring/alerts
# Cache status (requires API key)
GET /monitoring/cache
# Header: X-API-Key: YOUR_API_KEY- users - User accounts
- receipts - Food delivery receipts
- verification_codes - Email/password reset codes
- GIN index on JSONB items column
- Partial indexes for common queries
- Year column for future partitioning
- Connection pooling (20 connections in prod)
# Manual backup
./scripts/backup-database.sh
# Automated (add to crontab)
0 2 * * * /path/to/scripts/backup-database.shtests/test-full-suite.sh- Complete integration teststests/test-auth-comprehensive.sh- Authentication flowtests/test-pagination.sh- Pagination functionalitytests/test-load-balancing.sh- Multi-instance readinesstests/load-test-realistic.sh- Load testing (50 users, 1000 requests)
cd tests
./test-full-suite.shHow it works:
- Push code to your repository
- Railway automatically detects the push and deploys
- Railway uses environment variables configured in the Railway dashboard
- No code changes needed - production configuration is automatic
- Platform: Railway (validated)
- Database: PostgreSQL (Railway managed)
- Cache: Redis (Railway managed)
- Monitoring: Sentry APM
Configure these in the Railway dashboard:
NODE_ENV=production(usually set automatically)DATABASE_URL- Railway PostgreSQL connection string (auto-provided)JWT_SECRET- Secret key for JWT tokensAPI_KEY- API key for admin endpoints (database, monitoring) - Required for production securitySENTRY_DSN- Sentry error tracking (recommended)REDIS_URL- Railway Redis connection string (auto-provided if Redis service added)
- Configure environment variables in Railway dashboard
- Verify health checks:
curl https://your-app.railway.app/health - Monitor logs in Railway dashboard
snack-track/
├── src/
│ ├── config/ # App configuration, Redis, Sentry, logger
│ ├── models/ # TypeScript interfaces and types
│ ├── routes/ # API route handlers
│ ├── services/ # Business logic
│ │ ├── core/ # Cache, service container
│ │ ├── data/ # Database access layer
│ │ ├── email/ # Email processing
│ │ ├── import/ # CSV import services
│ │ └── receipt/ # Receipt processing
│ ├── middleware/ # Auth, pagination, error handling
│ └── index.ts # Application entry point
├── tests/ # Test suites
├── scripts/ # Utility scripts
├── MockUberData/ # Sample test data
├── docker-compose.yml # Development environment
└── README.md
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Test thoroughly
- Commit with clear messages
- Push and create a pull request
- Hot reload is enabled - changes auto-restart server
- Always test endpoints before committing
- Follow existing code patterns
- Update tests for new features
MIT License