🔍 Debugging a Production Next.js + Node.js App: From 404s to AWS S3 Integration
By Thanh Phong Le
January 27, 2025
🚀 The Application
Scalable Hackathon App — A full-stack resume management platform with AI-powered voice transcription
🔗 Live Demo: https://hackathon.thanhphongle.net/
Features:
- PDF resume upload & parsing
 - Voice resume recording with AI transcription
 - Real-time processing status tracking
 - Confidence scoring for parsed data
 
🎯 The Challenge
This hackathon application deployed on Vercel (frontend) and Railway (backend) had multiple production issues preventing core features from working.
🔍 Issues Discovered & Solutions
1. Database Column Mismatch (500 Error)
Symptom: Resume list page returned 500 error
Debug Commands:
# Checked Railway logs (revealed database query error)
# Created custom script to inspect DB schema:
node check-resume-columns.js
# Found: Column 'processing_status' exists in DB
# Checked the actual query in controller:
grep -A 30 "getUserResumes" src/controllers/resumeController.js
# Found: Query was selecting 'status' instead of 'processing_status'
Root Cause: SQL query referenced non-existent status column
Fix: Updated query to use processing_status as status
2. Missing Frontend Route (404 Error)
Symptom: Voice-resume page showed 404
Debug Commands:
# Checked if voice-resume directory exists:
ls -la frontend/app/\(dashboard\)/dashboard/ | grep -i voice
# Result: Empty - route didn't exist
# Verified directory structure:
tree frontend/app/\(dashboard\)/dashboard/ -I 'node_modules'
# Confirmed: Only 'resume' directory present, no 'voice-resume'
Root Cause: Route component was never created
Fix: Created /app/(dashboard)/dashboard/voice-resume/page.tsx
3. ES Module Import Error
Symptom: Build failed with "Element type is invalid"
Debug Command:
# Checked VoiceRecorder component export:
head -20 app/components/VoiceRecorder.tsx | grep -E "export|function|const.*VoiceRecorder"
# Found: export default function VoiceRecorder
Root Cause: Named import {VoiceRecorder} vs default export
Fix: Changed to default import: import VoiceRecorder from
4. CORS Blocking API Requests
Symptom: "Access-Control-Allow-Origin" error in console
Debug Commands:
# Checked CORS configuration in backend:
grep -n "cors\|CORS\|origin" src/server.js | head -20
# Found: origin: config.frontendUrl
# Checked how frontendUrl is configured:
grep -n "frontendUrl\|FRONTEND_URL" src/config/index.js
# Found: frontendUrl: process.env.FRONTEND_URL || 'http://localhost:3000'
# Tested CORS headers:
curl -I -H "Origin: https://hackathon.thanhphongle.net" https://dit-hackathon-backend-production.up.railway.app/api/v1/voiceresumes
# Initially: No Access-Control-Allow-Origin header
# After fix: access-control-allow-origin: https://hackathon.thanhphongle.net
Root Cause: Missing FRONTEND_URL environment variable in Railway
Fix: Added env var: FRONTEND_URL=https://hackathon.thanhphongle.net
5. AWS S3 Integration for Scalable File Storage
Implementation: Migrated from local file storage to AWS S3
Key Commands:
# Created S3 configuration:
cat > src/config/s3Upload.js << 'EOF'
# Updated unified upload config:
cat > src/config/unifiedUploadConfig.js << 'EOF'
# Set Railway environment variables:
AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_REGION, S3_BUCKET_NAME
🚀 Key Takeaways
Essential Debugging Tools:
- Railway/Vercel deployment logs — First source of truth
 curl -I— Quick HTTP header inspection- Browser DevTools Network/Console tabs — CORS and error analysis
 grep -nwith context (-A,-B) — Precise codebase searchingtreeandls -la— Verify file structure- Custom inspection scripts — Deep dive into database schema
 
Deployment Best Practices:
- Always check deployment logs first
 - Verify environment variables match across services
 - Use column aliases when DB schema differs from API contracts
 - Test CORS configuration with curl before debugging frontend
 - Build locally (
npm run build) before pushing - Implement storage abstraction for easy cloud migration
 
Tech Stack: Next.js 15, Node.js, PostgreSQL (Neon), AWS S3, Railway, Vercel
This debugging session transformed a broken production app into a fully functional, cloud-ready application with enterprise-grade file storage.
Try it yourself: https://hackathon.thanhphongle.net/
If you enjoyed this article, you can also find it published on LinkedIn and Medium.