EdTech Services is a Spring Boot REST API backend for an EdTech course platform. It provides APIs for course providers to create and manage courses, and for learners to search for courses by PIN code. The system uses JWT-based authentication and supports role-based access control (PROVIDER and LEARNER roles).
edtech.services/
βββ pom.xml # Parent POM
βββ README.md # This file
βββ JWT_AUTHENTICATION_FLOW.md # JWT authentication documentation
β
βββ edtech-db/ # Database Layer Module
β βββ pom.xml
β βββ src/main/java/com/task/edtech/db/
β βββ converter/ # Entity-DTO converters
β β βββ CourseConverter.java
β β βββ UserConverter.java
β βββ dto/ # Data Transfer Objects
β β βββ AuthResponse.java
β β βββ CourseDTO.java
β β βββ LoginRequest.java
β β βββ SearchFilters.java
β β βββ SignupRequest.java
β β βββ UserDTO.java
β βββ entity/ # JPA Entities
β β βββ BaseEntity.java
β β βββ Course.java
β β βββ User.java
β βββ enums/ # Enumerations
β β βββ CourseCategory.java
β β βββ CourseMode.java
β β βββ UserType.java
β βββ exception/ # Custom Exceptions
β β βββ EntityNotFoundException.java
β βββ repository/ # Spring Data JPA Repositories
β β βββ CourseRepository.java
β β βββ UserRepository.java
β βββ security/ # Security Utilities
β β βββ JwtUtil.java
β βββ service/ # Business Logic Services
β βββ AuthService.java
β βββ CourseService.java
β βββ UserService.java
β βββ impl/
β βββ AuthServiceImpl.java
β βββ CourseServiceImpl.java
β βββ UserServiceImpl.java
β
βββ edtech-api/ # REST API Layer Module
β βββ pom.xml
β βββ src/main/java/com/task/edtech/api/
β βββ controller/ # REST Controllers
β βββ AuthController.java
β βββ CourseController.java
β
βββ edtech-application/ # Application Layer Module
βββ pom.xml
βββ src/main/
βββ java/com/task/edtech/services/
β βββ Application.java # Main Spring Boot Application
β βββ config/ # Configuration Classes
β β βββ JpaConfig.java
β βββ security/ # Security Configuration
β βββ JwtAuthenticationFilter.java
β βββ SecurityConfig.java
βββ resources/
βββ application.properties
βββ application-local.properties # Local configuration (gitignored)
- edtech-db: Database layer containing entities, repositories, services, DTOs, converters, and security utilities
- edtech-api: REST API layer containing all REST controllers
- edtech-application: Application layer containing the main Spring Boot application class, security configuration, and application properties
edtech-db Module:
converter/- Converts between entities and DTOsdto/- Data Transfer Objects for API requests/responsesentity/- JPA entities representing database tablesenums/- Enumeration types (UserType, CourseMode, CourseCategory)exception/- Custom exception classesrepository/- Spring Data JPA repository interfacessecurity/- JWT utility classesservice/- Service interfaces and implementations
edtech-api Module:
controller/- REST controllers handling HTTP requests
edtech-application Module:
Application.java- Main Spring Boot application entry pointconfig/- Configuration classes (JPA, etc.)security/- Security configuration and filters
Before you begin, ensure you have the following installed:
-
Java 21 or higher
java -version
Should show version 21 or higher
-
Maven 3.6+
mvn -version
-
PostgreSQL 12+
psql --version
-
Git (for cloning the repository)
git --version
git clone <repository-url>
cd edtech.services-
Start PostgreSQL service
# Windows net start postgresql-x64-XX # Linux/Mac sudo systemctl start postgresql # or brew services start postgresql
-
Create the database
psql -U postgres
CREATE DATABASE edtech_db;
-
Verify database creation
psql -U postgres -d edtech_db -c "\dt"
-
Create local properties file (if not exists)
cd edtech-application/src/main/resources -
Edit
application-local.propertieswith your database credentials:spring.datasource.url=jdbc:postgresql://localhost:5432/edtech_db spring.datasource.username=postgres spring.datasource.password=your_postgres_password app.jwt.secret=your_jwt_secret_key_minimum_256_bits_for_hmac_sha_algorithms app.jwt.expiration=86400000
Important:
- Replace
your_postgres_passwordwith your actual PostgreSQL password - Generate a secure JWT secret (at least 256 bits/32 characters)
- Ensure
application-local.propertiesis in.gitignore
- Replace
-
Update
application.propertiesif needed:spring.profiles.active=local server.port=8080 spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=false spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
-
Navigate to project root
cd edtech.services -
Build all modules
mvn clean install
This will:
- Download all dependencies
- Compile all modules
- Run tests (if any)
- Package the application
-
Run from Maven (recommended for development)
cd edtech-application mvn spring-boot:runOr from the root directory:
mvn spring-boot:run -pl edtech-application
-
Run from JAR (after building)
java -jar edtech-application/target/edtech-application-0.0.1-SNAPSHOT.jar
-
Verify the application is running
- Check console logs for:
Started Application in X.XXX seconds - Open browser:
http://localhost:8080 - Check health:
http://localhost:8080/api/auth/signup(should return 400 Bad Request, not 404)
- Check console logs for:
-
Test Signup Endpoint
curl -X POST http://localhost:8080/api/auth/signup \ -H "Content-Type: application/json" \ -d '{ "name": "Test Provider", "email": "provider@test.com", "password": "password123", "userType": "PROVIDER" }'
-
Test Login Endpoint
curl -X POST http://localhost:8080/api/auth/login \ -H "Content-Type: application/json" \ -d '{ "email": "provider@test.com", "password": "password123" }'
-
Test Search Endpoint (public, no auth required)
curl http://localhost:8080/api/courses/search?pinCode=560066
- Java 21 - Programming language
- Spring Boot 4.0.1 - Application framework
- Spring Data JPA - Data persistence
- PostgreSQL - Relational database
- Spring Security - Authentication and authorization
- JWT (JSON Web Tokens) - Stateless authentication
- Maven - Build tool and dependency management
- Lombok - Boilerplate code reduction
βββββββββββββββββββββββ
β REST API Clients β
β (Postman, curl, β
β Frontend Apps) β
ββββββββββββ¬βββββββββββ
β HTTP/REST
βΌ
βββββββββββββββββββββββ
β Spring Boot β
β REST API β
β (Port 8080) β
ββββββββββββ¬βββββββββββ
β
βΌ
βββββββββββββββββββββββ
β PostgreSQL β
β Database β
β (Port 5432) β
βββββββββββββββββββββββ
User (1) ββββ< (Many) Course
Note: The system uses a unified User entity with a UserType enum to distinguish between PROVIDER and LEARNER roles. Only users with UserType.PROVIDER can create courses.
id(BIGINT, PRIMARY KEY, AUTO_INCREMENT)internal_id(UUID, UNIQUE, NOT NULL) - Public identifieremail(VARCHAR, UNIQUE, NOT NULL)password_hash(VARCHAR, NOT NULL) - encrypted passwordname(VARCHAR, NOT NULL) - user's name or organization nameuser_type(VARCHAR, NOT NULL) - "PROVIDER" or "LEARNER"created_at(TIMESTAMP, NOT NULL)updated_at(TIMESTAMP)
Indexes:
- Unique index on
internal_id - Unique index on
email - Index on
internal_idfor faster lookups - Index on
emailfor faster lookups
id(BIGINT, PRIMARY KEY, AUTO_INCREMENT)internal_id(UUID, UNIQUE, NOT NULL) - Public identifieruser_id(BIGINT, FOREIGN KEY β users.id, NOT NULL)title(VARCHAR, NOT NULL)description(TEXT)category(VARCHAR, NOT NULL) - Enum: CourseCategory valuesmode(VARCHAR, NOT NULL) - "ONLINE" or "IN_PERSON"address(VARCHAR) - required if mode is IN_PERSONpin_code(VARCHAR) - required if mode is IN_PERSONstart_date(DATE, NOT NULL)end_date(DATE) - nullable for single-session coursesschedule_info(VARCHAR) - e.g., "Mon-Fri 6-7pm"price_amount(DECIMAL) - nullable if freeis_free(BOOLEAN, DEFAULT false)capacity(INTEGER) - nullable, optionalis_published(BOOLEAN, DEFAULT false)created_at(TIMESTAMP, NOT NULL)updated_at(TIMESTAMP)
Indexes:
- Unique index on
internal_id - Unique index on
(user_id, title)- prevents duplicate course titles per user - Index on
internal_idfor faster lookups - Index on
(user_id, title)for faster queries
PROVIDER- Users who can create and manage coursesLEARNER- Users who can search and view courses
ONLINE- Online coursesIN_PERSON- In-person courses (requires address and PIN code)
- Defined in
CourseCategoryenum (e.g., YOGA, CODING, MUSIC, etc.)
http://localhost:8080/api
- JWT-based authentication
- Token sent in
Authorization: Bearer <token>header - Token expiration: 24 hours (configurable)
- Token contains user email and ID
-
POST /api/auth/signup
- Request Body:
{ "email": "string", "password": "string", "name": "string", "userType": "PROVIDER" | "LEARNER" } - Response:
{ "token": "string", "provider": { "id": number, "internalId": "uuid", "email": "string", "name": "string", "userType": "PROVIDER" | "LEARNER", "createdAt": "timestamp", "updatedAt": "timestamp" } } - Note: The response field is named
providerbut containsUserDTOdata
- Request Body:
-
POST /api/auth/login
- Request Body:
{ "email": "string", "password": "string" } - Response: Same as signup response
- Request Body:
-
GET /api/auth/me (Protected)
- Headers:
Authorization: Bearer <token> - Response:
{ "id": number, "internalId": "uuid", "email": "string", "name": "string", "userType": "PROVIDER" | "LEARNER", "createdAt": "timestamp", "updatedAt": "timestamp" }
- Headers:
-
POST /api/auth/logout (Protected)
- Headers:
Authorization: Bearer <token> - Response:
200 OK - Note: Clears server-side security context. Client should remove token from storage.
- Headers:
Note: All course management endpoints require authentication and only users with UserType.PROVIDER can create courses.
-
POST /api/courses
- Headers:
Authorization: Bearer <token> - Request Body: CourseDTO object (see below)
- Response: Created course object (CourseDTO)
- Validation: Only users with
userType: "PROVIDER"can create courses
- Headers:
-
GET /api/courses/mine
- Headers:
Authorization: Bearer <token> - Response: Array of user's courses (CourseDTO[])
- Headers:
-
GET /api/courses/{id}
- Headers:
Authorization: Bearer <token> - Response: Course detail object (CourseDTO)
- Security: Only returns course if user owns it
- Headers:
-
PUT /api/courses/{id}
- Headers:
Authorization: Bearer <token> - Request Body: CourseDTO object
- Response: Updated course object (CourseDTO)
- Security: Only allows update if user owns the course
- Headers:
-
DELETE /api/courses/{id}
- Headers:
Authorization: Bearer <token> - Response: 204 No Content
- Security: Only allows deletion if user owns the course
- Headers:
-
POST /api/courses/{id}/publish
- Headers:
Authorization: Bearer <token> - Response: Updated course with
isPublished: true(CourseDTO) - Security: Only allows publish if user owns the course
- Headers:
-
POST /api/courses/{id}/unpublish
- Headers:
Authorization: Bearer <token> - Response: Updated course with
isPublished: false(CourseDTO) - Security: Only allows unpublish if user owns the course
- Headers:
- GET /api/courses/search
- Query Parameters:
pinCode(optional): PIN code to search (prefix match)filterPinCode(optional): Override search PIN codecategory(optional): Filter by CourseCategory enum valuemode(optional): "ONLINE" or "IN_PERSON"isFree(optional): true/falsestartFrom(optional): Start date filter (YYYY-MM-DD format)startTo(optional): End date filter (YYYY-MM-DD format)
- Response: List of published courses (not paginated)
[ { "title": "Yoga for Beginners", "description": "Learn basic yoga poses", "category": "YOGA", "mode": "IN_PERSON", "address": "123 Main St", "pinCode": "110001", "startDate": "2025-02-01", "endDate": "2025-02-28", "scheduleInfo": "Mon-Fri 6-7pm", "priceAmount": 5000.00, "isFree": false, "capacity": 20 } ] - Note:
- Only returns published courses with
startDate >= today(orstartFromif provided) - If
pinCodeis not provided, returns all published courses matching other filters - Results are sorted by
startDateascending
- Only returns published courses with
- Query Parameters:
Signup Request:
{
"name": "John Doe",
"email": "john@example.com",
"password": "password123",
"userType": "PROVIDER"
}Course Creation Request (CourseDTO):
{
"title": "Yoga for Beginners",
"description": "Learn basic yoga poses",
"category": "YOGA",
"mode": "IN_PERSON",
"address": "123 Main St",
"pinCode": "110001",
"startDate": "2025-02-01",
"endDate": "2025-02-28",
"scheduleInfo": "Mon-Fri 6-7pm",
"priceAmount": 5000.00,
"isFree": false,
"capacity": 20
}Course Search Response:
[
{
"title": "Yoga for Beginners",
"description": "Learn basic yoga poses",
"category": "YOGA",
"mode": "IN_PERSON",
"address": "123 Main St",
"pinCode": "110001",
"startDate": "2025-02-01",
"endDate": "2025-02-28",
"scheduleInfo": "Mon-Fri 6-7pm",
"priceAmount": 5000.00,
"isFree": false,
"capacity": 20
}
]- Prefix Match: Courses where PIN code starts with the provided prefix
- Example: PIN "110001" matches "110001", "110002", "110003", etc.
- Only return courses where
start_date >= current_date(future courses only) - Only return courses where
is_published = true
- Default sort:
start_dateascending (earliest first) - Filters are applied in combination (AND logic)
- Date range filter:
startFromandstartTofilter courses bystart_date - All filters are optional (including
pinCode) - If
pinCodeis not provided, search returns all published courses matching other filters
IntelliJ IDEA Setup:
- Open project:
File > Open > Select edtech.services folder - Wait for Maven to import dependencies
- Enable annotation processing:
File > Settings > Build > Compiler > Annotation Processors > Enable - Configure Lombok: Install Lombok plugin if not already installed
- Set Java version:
File > Project Structure > Project > SDK: Java 21
- Use BCrypt for password hashing (never store plain passwords)
- Validate all inputs (use Jakarta Bean Validation)
- Sanitize user inputs to prevent SQL injection (JPA handles this)
- Use HTTPS in production
- Implement rate limiting for authentication endpoints (to be added)
- Validate JWT tokens properly
- Check ownership before allowing course modifications
- Verify userType before allowing course creation (only PROVIDER)
- Configure CORS appropriately for your API clients
- Never expose sensitive data in API responses
- Handle token expiration gracefully
spring.datasource.url=jdbc:postgresql://localhost:5432/edtech_db
spring.datasource.username=postgres
spring.datasource.password=your_password
app.jwt.secret=your_jwt_secret_key_minimum_256_bits_for_hmac_sha_algorithms
app.jwt.expiration=86400000Important:
- Keep
application-local.propertiesin.gitignore - Use environment variables or secure vaults in production
- Generate a strong JWT secret (minimum 32 characters/256 bits)
-
Error Handling
- Implement GlobalExceptionHandler
- Add structured error responses
- Implement error logging and monitoring
-
DevOps
- Set up CI/CD pipeline
- Configure automated testing
- Set up monitoring and alerting
- Implement logging aggregation