Welcome Contributors
Thank you for your interest in contributing to SkillRise! This guide will help you get started with the development workflow, coding standards, and submission process.
SkillRise is an open-source project built for students and educators. We welcome contributions of all kinds - bug fixes, features, documentation, and more.
Getting Started
Prerequisites
Before you begin, ensure you have the following installed:
Node.js Version 20 or higher required for both client and server
MongoDB Local instance or MongoDB Atlas connection
Git For version control and collaboration
Docker Optional, for containerized development
Required Accounts
You’ll need accounts for the following services:
Clerk - Authentication and user management
Stripe - Payment processing
Cloudinary - Media storage and CDN
Groq - AI-powered features
All services offer free tiers suitable for development. Use test/sandbox keys for local development.
Development Setup
1. Fork and Clone
Start by forking the repository and cloning your fork:
# Fork the repo on GitHub first, then:
git clone https://github.com/YOUR_USERNAME/skillrise.git
cd skillrise
# Add upstream remote
git remote add upstream https://github.com/pv-pushkarverma/skillrise.git
2. Install Dependencies
Install packages for both client and server:
# Install client dependencies
cd client
npm install
# Install server dependencies
cd ../server
npm install
Create .env files with your credentials:
MONGODB_URI = mongodb://localhost:27017/skillrise
CURRENCY = INR
CLERK_PUBLISHABLE_KEY = pk_test_...
CLERK_SECRET_KEY = sk_test_...
CLERK_WEBHOOK_SECRET = whsec_...
CLOUDINARY_NAME = your_cloud_name
CLOUDINARY_API_KEY = your_api_key
CLOUDINARY_SECRET_KEY = your_api_secret
STRIPE_PUBLISHABLE_KEY = pk_test_...
STRIPE_SECRET_KEY = sk_test_...
STRIPE_WEBHOOK_SECRET = whsec_...
GROQ_CHATBOT_API_KEY = gsk_...
GROQ_MODEL = llama-3.3-70b-versatile
Never commit .env files or expose API keys in your code. These files are already in .gitignore.
4. Seed the Database (Optional)
Populate your local database with demo data:
This creates:
5 demo educators and 5 demo students
5 sample courses with chapters and lectures
Community groups, posts, and replies
Quizzes and sample enrollment data
5. Run the Development Servers
Start both frontend and backend in separate terminals:
Client (Terminal 1)
Server (Terminal 2)
cd client
npm run dev
# Vite dev server starts at http://localhost:5173
The client dev server proxies API requests to http://localhost:3000 as configured in VITE_BACKEND_URL.
6. Set Up Webhooks (Optional)
For testing authentication and payments locally, forward webhooks using ngrok:
# Install ngrok
npm install -g ngrok
# Forward port 3000
ngrok http 3000
Then configure webhook URLs in your service dashboards:
Service Endpoint Events Clerk https://your-ngrok-url.ngrok.io/clerkuser.created, user.updatedStripe https://your-ngrok-url.ngrok.io/stripecheckout.session.completed
Development Workflow
Branch Strategy
SkillRise follows a Git Flow-inspired branching model:
main (production)
↑
└─ dev (development)
↑
├─ feature/add-user-profile
├─ fix/quiz-scoring-bug
└─ refactor/api-error-handling
Create a Feature Branch
Always branch from dev, never from main: git checkout dev
git pull upstream dev
git checkout -b feature/your-feature-name
Make Your Changes
Write code, add tests, update documentation as needed.
Commit Your Work
Use clear, descriptive commit messages: git add .
git commit -m "Add user profile editing functionality"
Push to Your Fork
git push origin feature/your-feature-name
Open a Pull Request
Create a PR from your fork to the dev branch of the main repository.
Commit Message Convention
Use clear, imperative commit messages:
Good Examples
Bad Examples
✓ Add quiz timer functionality
✓ Fix course enrollment redirect
✓ Refactor authentication middleware
✓ Update educator dashboard UI
✓ Remove deprecated API endpoints
Code Standards
SkillRise uses ESLint for code quality and Prettier for consistent formatting.
cd client
# Run ESLint
npm run lint
# Fix auto-fixable issues
npm run lint:fix
# Check formatting
npm run format:check
# Format all files
npm run format
cd server
# Run ESLint
npm run lint
# Fix auto-fixable issues
npm run lint:fix
# Check formatting
npm run format:check
# Format all files
npm run format
All PRs must pass ESLint and Prettier checks. The CI pipeline will automatically verify this.
JavaScript/JSX Style Guide
Component Structure
import { useState , useEffect } from 'react'
import PropTypes from 'prop-types'
/**
* CourseCard component displays course information
* @param {Object} course - Course data object
* @param {Function} onEnroll - Enrollment callback
*/
const CourseCard = ({ course , onEnroll }) => {
const [ isLoading , setIsLoading ] = useState ( false )
useEffect (() => {
// Effect logic
}, [])
const handleEnroll = async () => {
setIsLoading ( true )
try {
await onEnroll ( course . id )
} catch ( error ) {
console . error ( 'Enrollment failed:' , error )
} finally {
setIsLoading ( false )
}
}
return (
< div className = "course-card" >
{ /* Component JSX */ }
</ div >
)
}
CourseCard . propTypes = {
course: PropTypes . object . isRequired ,
onEnroll: PropTypes . func . isRequired ,
}
export default CourseCard
API Controllers
import Course from '../models/Course.js'
/**
* Get all published courses
* @route GET /api/course
*/
export const getAllCourses = async ( req , res ) => {
try {
const courses = await Course . find ({ published: true })
. populate ( 'educator' , 'name imageUrl' )
. sort ({ createdAt: - 1 })
res . status ( 200 ). json ({
success: true ,
data: courses ,
})
} catch ( error ) {
console . error ( 'Error fetching courses:' , error )
res . status ( 500 ). json ({
success: false ,
message: 'Failed to fetch courses' ,
})
}
}
File Naming Conventions
Components : PascalCase with .jsx extension
✓ CourseCard.jsx
✓ UserProfile.jsx
Hooks : camelCase with use prefix
✓ useTimeTracker.js
✓ useInView.js
Utils : camelCase
✓ formatDate.js
✓ apiClient.js
Controllers : camelCase with Controller suffix
✓ courseController.js
✓ userController.js
Models : PascalCase
Routes : camelCase with Routes suffix
✓ courseRoutes.js
✓ adminRoutes.js
Pull Request Process
Before Submitting
PR Template
When creating a pull request, include:
## Description
[Brief description of what this PR does]
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
## Testing
[How you tested the changes]
## Screenshots (if applicable)
[Add screenshots for UI changes]
## Related Issues
Closes #[issue number]
CI Checks
All pull requests trigger automated CI checks via GitHub Actions:
Client Build Job
Install dependencies (npm ci)
Run ESLint (npm run lint)
Check Prettier formatting (npm run format:check)
Build the app (npm run build)
Server Lint Job
Install dependencies (npm ci)
Run ESLint (npm run lint)
Check Prettier formatting (npm run format:check)
PRs cannot be merged if CI checks fail. Fix all issues before requesting review.
Review Process
Automated Checks : CI pipeline must pass
Code Review : At least one maintainer approval required
Testing : Reviewers may test changes locally
Merge : Once approved, maintainers will merge to dev
Development Commands Reference
Client Commands
cd client
# Development
npm run dev # Start Vite dev server (http://localhost:5173)
npm run build # Build for production
npm run preview # Preview production build
# Code Quality
npm run lint # Run ESLint
npm run lint:fix # Fix auto-fixable ESLint issues
npm run format # Format code with Prettier
npm run format:check # Check if code is formatted
Server Commands
cd server
# Development
npm run server # Start with nodemon (hot reload)
npm start # Start in production mode
npm run seed # Populate database with demo data
# Code Quality
npm run lint # Run ESLint
npm run lint:fix # Fix auto-fixable ESLint issues
npm run format # Format code with Prettier
npm run format:check # Check if code is formatted
Docker Commands
# Build and run both services
docker compose up --build
# Run in detached mode
docker compose up -d
# View logs
docker compose logs -f
docker compose logs -f server # Server logs only
docker compose logs -f client # Client logs only
# Run seed script in container
docker compose exec server node seed.js
# Stop all services
docker compose down
# Remove volumes
docker compose down -v
Common Development Tasks
Adding a New API Route
Create Controller Function
Add logic in appropriate controller file: // server/controllers/courseController.js
export const getCourseByCategoryController = async ( req , res ) => {
try {
const { category } = req . params
const courses = await Course . find ({ category , published: true })
res . status ( 200 ). json ({ success: true , data: courses })
} catch ( error ) {
res . status ( 500 ). json ({ success: false , message: error . message })
}
}
Define Route
Add route in corresponding routes file: // server/routes/courseRoute.js
import { getCourseByCategoryController } from '../controllers/courseController.js'
router . get ( '/category/:category' , getCourseByCategoryController )
Test Endpoint
Use Postman or curl to test: curl http://localhost:3000/api/course/category/development
Adding a New React Component
Create Component File
// client/src/components/student/CourseFilter.jsx
import { useState } from 'react'
const CourseFilter = ({ onFilterChange }) => {
const [ category , setCategory ] = useState ( '' )
return (
< select onChange = { ( e ) => onFilterChange ( e . target . value ) } >
< option value = "" > All Categories </ option >
< option value = "development" > Development </ option >
< option value = "design" > Design </ option >
</ select >
)
}
export default CourseFilter
Import and Use
import CourseFilter from '../components/student/CourseFilter'
const CoursesPage = () => {
const handleFilter = ( category ) => {
// Filter logic
}
return < CourseFilter onFilterChange = { handleFilter } />
}
Adding Environment Variables
Update .env Files
Add the variable to appropriate .env file(s)
Document in README
Update the environment variables section in documentation
Add to CI/CD
If needed for builds, add to GitHub Secrets and workflow files
Troubleshooting
If you see “Port 3000 already in use” or “Port 5173 already in use”: # Find and kill process on port 3000
lsof -ti:3000 | xargs kill -9
# Find and kill process on port 5173
lsof -ti:5173 | xargs kill -9
MongoDB connection failed
Ensure MongoDB is running: # Check if MongoDB is running
mongosh
# Or start MongoDB service
brew services start mongodb-community # macOS
sudo systemctl start mongod # Linux
Reinstall dependencies: # Remove node_modules and reinstall
rm -rf node_modules package-lock.json
npm install
Clerk authentication not working
Verify:
Publishable key matches in both .env files
Webhook secret is correct
ngrok URL is configured in Clerk dashboard (for local webhooks)
Getting Help
Remember: Good code is code that others can understand. Write clear, maintainable code and document complex logic.