- Spring Boot 3.5 API wrapper around LeetCode GraphQL with a local SQLite cache/database.
- Main app entry:
src/main/java/com/rajat_singh/leetcode_api/LeetcodeApiApplication.java(@EnableCaching,@EnableScheduling). - Runtime config:
src/main/resources/application.yml(DB path, rate limits, scheduler toggle, Swagger, actuator).
- Request flow is
controller -> service -> client/repository -> DTO, with controllers staying thin (seecontroller/UserController.java). - Outbound LeetCode calls are centralized in
client/LeetCodeClient.java; GraphQL query strings live ingraphql/GraphQlQueries.java. - Contest history uses a DB-first fallback pattern: read
contest_history; if missing, fetch from LeetCode and persist (seeservice/LeetCodeContestService.java#getUserContestRankingHistory). - Questions endpoints are local-DB-backed; data is refreshed by scheduler jobs, not by direct fetch in request handlers (
service/LeetCodeQuestionsService.java,scheduler/LeetCodeSyncScheduler.java). - Mapping between persistence and API DTOs is MapStruct-based (
mappers/QuestionMapper.java,mappers/ContestMapper.java).
- SQLite file DB at
./data/leetcode.db; JPA auto-update is enabled (spring.jpa.hibernate.ddl-auto: update). - Core tables/entities:
leetcode_questions(entity/QuestionEntity.java),contest_history(entity/UserContestHistoryEntity.java),contest_data+sponsors(entity/ContestDataEntity.java,entity/SponsorEntity.java). - Topic tags are stored via
@ElementCollectioninquestion_topic_tags; queries join this collection (repository/QuestionsRepository.java). - Repository layer contains important native SQL/business queries (for example
findBiggestRatingJumpCTE inrepository/ContestHistoryRepository.java).
- Contest endpoints are cached with Caffeine (
contestHistoryCache,contestRankingCache,contestRankingWithHistoryCache) inconfig/CacheConfig.java. - Cache eviction triggers DB cleanup through
ContestHistoryCleanupService(removal listener inCacheConfig). - Scheduler jobs are guarded by
scheduler.enabled; they sync full questions weekly, AC rates daily, POTD daily, contests monthly (scheduler/LeetCodeSyncScheduler.java). - Outbound LeetCode calls are throttled via Resilience4j
@RateLimiter(name = "leetcode-api")on client methods. - Inbound API throttling for question endpoints is configured with Bucket4j in
application.yml.
- Errors are standardized as
ApiErrorResponsefromexceptions/GlobalExceptionHandler.java. - Custom validation exceptions are expected for domain filters (
InvalidTrendDirection,InvalidContestTime,BadRequestException). DELETE /api/v1/users/{username}/contests/evictUserDatarequires local request origin or validX-API-KEYmatched toapp.api.secret(controller/UserContestInfoController.java).
- Run app:
./mvnw spring-boot:run - Run tests:
./mvnw -q test(currently only context-load test insrc/test/java/.../LeetcodeApiApplicationTests.java). - API docs:
/swagger-ui/index.html; health/metrics endpoints are exposed under/actuatorperapplication.yml.
- When adding a new LeetCode-backed endpoint, first add GraphQL in
GraphQlQueries, then a typed client method, then service orchestration, then controller route. - Reuse existing DTO/entity naming style (
*Response,*DTO,*Entity) and keep business logic in services, not controllers. - For question search features, implement filters in
QuestionSpecificationServicebefore touching controller logic. - If changing contest cache behavior, verify both cache TTL logic and DB eviction side effects in
CacheConfig/ContestHistoryCleanupService.