- Setup & Installation
- Architecture
- API Utilities
- Error Handling
- Input Validation
- Security
- Database Operations
- Logging
- Environment Configuration
- Feature branches:
feature/feature-namefor new features - Bug fix branches:
bugfix/issue-descriptionfor fixes - Documentation branches:
docs/doc-topicfor doc updates - Release branches:
release/version-numberfor releases
- Create a feature branch from
main:git checkout -b feature/your-feature-name
- Keep your branch updated by regularly rebasing on
main:git fetch origin git rebase origin/main
- Push your changes and create a Pull Request when ready:
git push origin feature/your-feature-name
- After PR review, merge to
mainusing "Squash and merge" for cleaner history
This approach reduces merge conflicts and keeps the main branch history clean and navigable.
- Node.js 18+ or higher
- npm or yarn package manager
- MongoDB (optional, for persistent storage)
# Clone the repository
git clone https://github.com/Alfredbis29/HireMe.AI.git
cd HireMe.AI
# Install dependencies
npm install
# Set up environment variables
cp .env.example .env.local
# Generate NEXTAUTH_SECRET
openssl rand -base64 32
# Update .env.local with your configuration
# Required:
# - NEXTAUTH_SECRET (generated above)
# - NEXTAUTH_URL (http://localhost:3000 for development)
# Optional:
# - OPENAI_API_KEY (for resume analysis)
# - GOOGLE_CLIENT_ID and GOOGLE_CLIENT_SECRET (for OAuth)
# - LINKEDIN_CLIENT_ID and LINKEDIN_CLIENT_SECRET (for LinkedIn integration)
# Run development server
npm run dev
# Visit http://localhost:3000HireMe.AI/
├── app/ # Next.js App Router
│ ├── api/ # API routes and endpoints
│ ├── auth/ # Authentication pages
│ ├── upload/ # Resume upload interface
│ └── results/ # Analysis results display
├── components/ # Reusable React components
├── lib/ # Utility functions and helpers
│ ├── validation.ts # Input validation utilities
│ ├── error-handler.ts # Error handling utilities
│ ├── request-logger.ts # Request/response logging
│ ├── security.ts # Security headers and rate limiting
│ ├── env.ts # Environment configuration
│ ├── db-retry.ts # Database retry logic
│ ├── db.ts # Database operations
│ └── auth.ts # Authentication utilities
├── types/ # TypeScript type definitions
└── public/ # Static assets
- Framework: Next.js 14 with App Router
- Language: TypeScript
- Styling: Tailwind CSS + shadcn/ui
- Authentication: NextAuth.js
- Database: MongoDB / In-Memory (dev)
- API Integration: OpenAI (resume analysis), LinkedIn, Google OAuth
- Form Handling: React Hook Form
- File Upload: Multer + React Dropzone
import { NextRequest, NextResponse } from 'next/server'
import { withSecurityHeaders } from '@/lib/security'
import { withLogging, createLogContext } from '@/lib/request-logger'
import { sendErrorResponse, AppError, ErrorCodes } from '@/lib/error-handler'
import { validateEmail, validatePassword } from '@/lib/validation'
export const POST = withSecurityHeaders(
withLogging(async (request: NextRequest, context) => {
try {
const body = await request.json()
// Validate input
const emailValidation = validateEmail(body.email)
if (!emailValidation.isValid) {
throw new AppError(
ErrorCodes.VALIDATION_ERROR,
400,
'Invalid email',
{ errors: emailValidation.errors }
)
}
// Your logic here
const result = { success: true, data: 'Your data' }
return NextResponse.json(result)
} catch (error) {
return sendErrorResponse(error instanceof Error ? error : new Error(String(error)), request)
}
})
)// withSecurityHeaders - Adds security headers automatically
// withLogging - Logs request/response with request ID
// withRetry - Adds automatic retry logic with exponential backoff (for async operations)
// Combine multiple wrappers
export const GET = withSecurityHeaders(withLogging(myHandler))import { AppError, ErrorCodes, createValidationError, createAuthenticationError } from '@/lib/error-handler'
// Standard HTTP errors
throw new AppError(ErrorCodes.VALIDATION_ERROR, 400, 'Invalid input', { field: 'email' })
throw new AppError(ErrorCodes.NOT_FOUND, 404, 'User not found')
throw new AppError(ErrorCodes.AUTHENTICATION_ERROR, 401, 'Unauthorized')
// Specialized error factories
throw createValidationError('Email is invalid', { field: 'email' })
throw createAuthenticationError('Session expired')
throw createAuthorizationError('Insufficient permissions')
throw createDatabaseError('Connection failed')All errors follow a consistent format:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid email",
"statusCode": 400,
"details": { "field": "email" },
"timestamp": "2024-01-15T10:30:00Z"
}
}import {
validateEmail,
validatePassword,
validateName,
validateFileUpload,
sanitizeString
} from '@/lib/validation'
// Email validation
const emailResult = validateEmail('user@example.com')
if (!emailResult.isValid) {
console.log(emailResult.errors) // Array of error messages
}
// Password validation (min 8 chars, uppercase, lowercase, number)
const passwordResult = validatePassword('MyPassword123')
// Name validation (2-100 chars, letters/spaces/hyphens only)
const nameResult = validateName('John Doe')
// File upload validation
const fileResult = validateFileUpload(file, 5, ['application/pdf', 'application/msword'])
// XSS prevention
const safe = sanitizeString(userInput)interface ValidationResult {
isValid: boolean
errors: string[] // Detailed error messages
}All responses automatically include:
X-Frame-Options: DENY- Prevent clickjackingX-XSS-Protection: 1; mode=block- Browser XSS protectionX-Content-Type-Options: nosniff- Prevent MIME sniffingReferrer-Policy: strict-origin-when-cross-originPermissions-Policy: geolocation=(), microphone=(), camera=()Strict-Transport-Security: max-age=31536000(production only)
import { checkRateLimit, rateLimitConfigs } from '@/lib/security'
// Check rate limit
const result = checkRateLimit(request, rateLimitConfigs.auth)
if (!result.allowed) {
return NextResponse.json(
{ error: 'Rate limit exceeded' },
{ status: 429 }
)
}
// Use config for your endpoint
export const POST = async (request: NextRequest) => {
const limit = checkRateLimit(request, rateLimitConfigs.api)
// ... rest of handler
}Configured in lib/security.ts. Update NEXTAUTH_URL and ALLOWED_ORIGINS environment variables.
import { executeWithRetry, defaultRetryConfig } from '@/lib/db-retry'
const user = await executeWithRetry(
() => findUserByEmail(email),
defaultRetryConfig,
'Find user by email'
)
// Custom retry configuration
const customConfig = {
maxRetries: 5,
initialDelayMs: 50,
maxDelayMs: 10000,
backoffMultiplier: 2
}
const result = await executeWithRetry(operation, customConfig)import { CircuitBreaker, executeWithCircuitBreaker } from '@/lib/db-retry'
const dbCircuitBreaker = new CircuitBreaker(
5, // Failure threshold
60000 // Reset timeout in ms
)
// Use in operations
const result = await executeWithCircuitBreaker(
() => database.query(...),
dbCircuitBreaker,
'Database query'
)import { ConnectionPool } from '@/lib/db-retry'
const pool = new ConnectionPool(
10, // Max connections
30000 // Health check interval
)
// Get pool statistics
const stats = pool.getStats()
console.log(`Pool utilization: ${stats.utilization.toFixed(2)}%`)import { withLogging, logRequest, logResponse, logError } from '@/lib/request-logger'
// Automatic logging with middleware
export const GET = withLogging(async (request, context) => {
// context contains: requestId, timestamp, method, path
logRequest(context, 'Processing request', { userId: '123' })
// ... do work ...
logResponse(context, 200, 'Successfully processed', duration)
return NextResponse.json({ success: true })
})
// Manual logging
const context = createLogContext(request)
logError(context, new Error('Database failed'), { table: 'users' })[2024-01-15T10:30:00Z] [1705317000000-abc123def] [INFO] POST /api/users (125ms) - Request received | {"userId":"123"}
import { validateEnvironment, isDevelopment, isProduction } from '@/lib/env'
// Validate at startup
try {
const config = validateEnvironment()
console.log('✅ Environment is valid')
} catch (error) {
console.error('❌ Environment validation failed:', error.message)
process.exit(1)
}
// Environment checks
if (isDevelopment()) {
// Dev-only code
}
if (isProduction()) {
// Prod-only code
}# Required
NODE_ENV=development
NEXTAUTH_SECRET=<generate with: openssl rand -base64 32>
NEXTAUTH_URL=http://localhost:3000
# Optional but recommended
OPENAI_API_KEY=sk-...
DATABASE_URL=mongodb://...
# Optional OAuth providers (both required if using)
GOOGLE_CLIENT_ID=...
GOOGLE_CLIENT_SECRET=...
LINKEDIN_CLIENT_ID=...
LINKEDIN_CLIENT_SECRET=...
# Optional
DEBUG=false
ALLOWED_ORIGINS=https://example.com,https://app.example.comtry {
// Your code
} catch (error) {
return sendErrorResponse(error, request)
}const validation = validateEmail(input)
if (!validation.isValid) {
throw createValidationError('Invalid email', { errors: validation.errors })
}const user = await executeWithRetry(
() => findUser(id),
undefined,
'Find user'
)const limit = checkRateLimit(request, rateLimitConfigs.auth)
if (!limit.allowed) {
throw new AppError(ErrorCodes.RATE_LIMIT_ERROR, 429, 'Too many attempts')
}const context = createLogContext(request)
logRequest(context, 'User registration started', { email: sanitizeString(email) })
// ... process ...
logResponse(context, 200, 'User registered', duration)const cleanName = sanitizeString(userInput)- Enable
noImplicitAny - Enable
strictNullChecks - Use proper typing for all functions
npm testCreate test files in __tests__ directories:
import { POST } from '@/app/api/users/route'
import { NextRequest } from 'next/server'
describe('/api/users', () => {
it('should create a user', async () => {
const request = new NextRequest('http://localhost:3000/api/users', {
method: 'POST',
body: JSON.stringify({ email: 'test@example.com', password: 'Test123' })
})
const response = await POST(request)
expect(response.status).toBe(200)
})
})# Check environment variables
node -e "console.log(process.env.NEXTAUTH_SECRET ? '✅ Set' : '❌ Not set')"
# Generate NEXTAUTH_SECRET
openssl rand -base64 32- Check
NEXTAUTH_URLmatches your domain - Verify MongoDB connection string
- Check retry logs for connection attempts
- Review circuit breaker state with
getStats()
Check X-RateLimit-Remaining and X-RateLimit-Reset headers in response.
Run npm run build to check for TypeScript errors.
When adding new features:
- ✅ Add proper TypeScript types
- ✅ Add input validation
- ✅ Add error handling
- ✅ Add structured logging
- ✅ Add security headers
- ✅ Update this documentation
For issues or questions:
- Check the GitHub Issues
- Review documentation in
/docs - Check environment validation errors
Comprehensive testing framework for authentication and job matching features.