Debugging a Production Next.js + Node.js App: From 404s to AWS S3 Integration

🔍 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 -n with context (-A, -B) — Precise codebase searching
  • tree and ls -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.