A production-ready questionnaire system built with Next.js frontend and Express backend, featuring MySQL database integration via Prisma ORM.
🚀 Features
- Frontend: Modern Next.js application with TypeScript and Tailwind CSS
- Backend: Express API with comprehensive validation and security
- Database: MySQL integration with Prisma ORM
- Security: Rate limiting, CORS protection, input sanitization
- Validation: Client and server-side validation with Zod schemas
- Responsive: Mobile-friendly UI with modern design
🏗️ Architecture
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Next.js │ │ Express API │ │ MySQL │
│ Frontend │◄──►│ Backend │◄──►│ Database │
│ (Port 3000) │ │ (Port 4000) │ │ (AWS RDS) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
📋 Prerequisites
- Node.js 18+
- npm or yarn
- MySQL database (AWS RDS or local)
- Git
🛠️ Installation
1. Clone the repository
git clone <repository-url>
cd questionnaire-system
2. Install dependencies
npm run install:all
3. Environment Configuration
Backend Configuration
Create a .env file in the backend directory:
cd backend
cp env.example .env
Edit the .env file with your database credentials:
# Database connection string for MySQL (AWS RDS)
DATABASE_URL="mysql://username:password@your-aws-rds-endpoint:3306/mini_form"
# Server configuration
PORT=4000
NODE_ENV=development
# CORS configuration
CORS_ORIGIN=http://localhost:3000
# Security configuration
IP_SALT=your-random-salt-here-change-this-in-production
# Rate limiting
RATE_LIMIT_WINDOW_MS=60000
RATE_LIMIT_MAX_REQUESTS=60
Frontend Configuration
Create a .env.local file in the frontend directory:
cd ../frontend
touch .env.local
Add the following content:
NEXT_PUBLIC_API_BASE_URL=http://localhost:4000
4. Database Setup
Using AWS RDS MySQL
- Create a MySQL database in AWS RDS
- Note the endpoint, port, username, and password
- Update the
DATABASE_URLin your backend.envfile - Create a database named
mini_form
Local MySQL (Alternative)
# Install MySQL locally or use Docker
docker run --name mysql-questionnaire \
-e MYSQL_ROOT_PASSWORD=root \
-e MYSQL_DATABASE=mini_form \
-e MYSQL_USER=user \
-e MYSQL_PASSWORD=pass \
-p 3306:3306 \
-d mysql:8.0
5. Database Migration
cd backend
# Generate Prisma client
npm run db:generate
# Run database migrations
npm run db:migrate
# (Optional) Seed with sample data
npm run db:seed
🚀 Running the Application
Development Mode
# Start both frontend and backend
npm run dev
# Or start them separately:
npm run dev:frontend # Frontend on port 3000
npm run dev:backend # Backend on port 4000
Production Mode
# Build the application
npm run build
# Start production servers
npm run start
📱 Usage
1. Access the Application
- Frontend: http://localhost:3000
- Backend API: http://localhost:4000
2. Fill Out the Questionnaire
- Navigate to the questionnaire form
- Answer the questions (mix of single choice, multi-choice, and text)
- Submit your responses
3. View Confirmation
- After successful submission, you'll be redirected to a thank you page
- Note your response ID for future reference
🔌 API Endpoints
POST /api/v1/responses
Create a new questionnaire response.
Request:
{
"answers": {
"q1": "A",
"q2": ["X", "Y"],
"q3": "Additional comments"
}
}
Response:
{
"id": "ckxyz...",
"createdAt": "2025-01-23T10:00:00Z"
}
GET /api/v1/responses/:id
Retrieve a specific response by ID.
GET /api/v1/metrics
Get aggregated metrics for all responses.
Query Parameters:
- from: Start date (ISO format)
- to: End date (ISO format)
Response:
{
"totalSubmissions": 123,
"counts": {
"q1": { "A": 40, "B": 60, "C": 23 },
"q2": { "X": 70, "Y": 53, "Z": 12 }
}
}
🧪 Testing
Backend Tests
cd backend
# Run all tests
npm test
# Run tests in watch mode
npm run test:watch
# Run tests with coverage
npm test -- --coverage
Frontend Tests
cd frontend
# Run linting
npm run lint
# Type checking
npm run type-check
📊 Database Schema
The system uses a simple schema with a single Response table:
model Response {
id String @id @default(cuid())
answers Json // Question answers as JSON
userAgent String? // Optional user agent
ipHash String? // Hashed IP address for privacy
createdAt DateTime @default(now())
}
🔒 Security Features
- Input Validation: Comprehensive validation using Zod schemas
- Rate Limiting: 60 requests per minute per IP
- CORS Protection: Restricted to specified origins
- Input Sanitization: XSS protection and HTML stripping
- IP Hashing: Privacy-preserving IP address storage
- Request IDs: Unique identifiers for request tracking
🚀 Deployment
Backend Deployment
- Build the application:
npm run build - Set production environment variables
- Deploy to your preferred hosting service (Heroku, AWS, etc.)
Frontend Deployment
- Build the application:
npm run build - Deploy to Vercel, Netlify, or your preferred hosting service
- Update the
NEXT_PUBLIC_API_BASE_URLto point to your production backend
Database Deployment
- Use AWS RDS MySQL for production
- Ensure proper security groups and network access
- Regular backups and monitoring
🐛 Troubleshooting
Common Issues
Database Connection Issues
- Verify your
DATABASE_URLis correct - Ensure the database is accessible from your application
- Check firewall and security group settings
CORS Errors
- Verify
CORS_ORIGINis set correctly - Ensure frontend and backend ports match
Rate Limiting
- Check if you're hitting the rate limit (60 req/min)
- Adjust limits in
.envif needed
Logs
- Backend logs are displayed in the console
- Check for request IDs in error messages
- Use
x-request-idheader for request tracking
📈 Monitoring
Health Check
- Endpoint:
GET /health - Returns server status and timestamp
Metrics
- Use the metrics endpoint to monitor response patterns
- Track total submissions and answer distributions
🤝 Contributing
- Fork the repository
- Create a feature branch
- Make your changes
- Add tests for new functionality
- Submit a pull request
📄 License
This project is licensed under the MIT License.
🆘 Support
For support and questions: - Check the troubleshooting section - Review the API documentation - Create an issue in the repository
Built with ❤️ using Next.js, Express, and Prisma