Thank you for your interest in contributing! This document explains how to get started, what to expect during the review process, and the conventions this project follows.
- Getting Started
- Making Changes
- Submitting a Pull Request
- Reporting Issues
- Project Architecture
- Legal
| Requirement | Version | Notes |
|---|---|---|
| JDK | 21+ | Required |
| Git | 2.x+ | For cloning and contributing |
| IntelliJ IDEA | Latest | Recommended IDE (Lombok and Gradle support built-in) |
-
Fork and clone the repository
Fork the repository, then clone your fork:
git clone https://github.com/<your-username>/server-api.git cd server-api
-
Build the project
The Gradle wrapper is included - no separate Gradle installation is needed.
cd server-api ./gradlew build -
Open in IntelliJ IDEA
Open the project root as a Gradle project. IntelliJ will automatically detect the
build.gradle.ktsand import dependencies. Ensure the Lombok plugin is installed and annotation processing is enabled (Settings > Build > Compiler > Annotation Processors). -
Verify the setup
./gradlew test
- Create a feature branch from
masterfor your work. - Use a descriptive branch name:
fix/rate-limit-window,feat/custom-error-body,docs/versioning-examples.
git checkout -b feat/my-feature master- Spring conventions - Follow standard Spring Boot patterns for
@Configuration,@RestControllerAdvice,HandlerInterceptor, andRequestMappingHandlerMapping. - Collections - Always use
Concurrent.newList(),Concurrent.newMap(),Concurrent.newSet()instead ofnew ArrayList,new HashMap, etc. - Annotations - Use
@NotNull/@Nullablefromorg.jetbrains.annotationson all public method parameters and return types. - Lombok - Use
@Getter,@RequiredArgsConstructor,@Log4j2, etc. where appropriate. The logger field is non-static (lombok.log.fieldIsStatic = false). - Builder pattern - Use
ClassBuilder<T>with@BuildFlagvalidation. Follow the existing pattern inServerConfig.Builder.
- Omit curly braces when the
ifbody is a single line. - Use curly braces when the body wraps across multiple lines.
- Class level - Noun phrase describing what the type is.
- Method level - Active verb, third person singular, describing what the method does.
- Tags - Always include
@param,@return,@throwson public methods. Tag descriptions are lowercase sentence fragments with no trailing period. Single space after the param name (no column alignment). - Punctuation - Only use single hyphens (
-) as separators. Never em dashes,—, or double hyphens. - Never use
@authoror@since.
- All server exceptions extend
ServerExceptionand carry anHttpStatus. - Leaf exceptions internalize their messages - constructors accept only domain-specific parameters and format the message internally.
- Follow the five-constructor pattern documented in the root
CLAUDE.md.
Write clear, concise commit messages that describe what changed and why.
Add rate limit headers to 429 responses
Includes X-RateLimit-Remaining and Retry-After headers when the
sliding window counter rejects a request.
- Use the imperative mood ("Add", "Fix", "Update", not "Added", "Fixes").
- Keep the subject line under 72 characters.
- Add a body when the why isn't obvious from the subject.
Tests use JUnit 5 (Jupiter) with Spring Boot Test for integration tests:
./gradlew test- Add tests for new functionality where practical.
- Unit tests for interceptors, services, and configuration don't require a
running Spring context - prefer plain JUnit tests over
@SpringBootTestwhere possible. - The test source set includes a
TestServerand example controllers (TestApiKeyController,TestVersionController) for exercising the framework. RunTestServer.main()from your IDE to manually verify changes. - When adding a new framework feature, add example endpoints to the test
controllers or create new ones under
src/test/java/dev/sbs/serverapi/controller/to demonstrate and verify the feature.
-
Push your branch to your fork.
git push origin feat/my-feature
-
Open a Pull Request against the
masterbranch of SkyBlock-Simplified/server-api. -
In the PR description, include:
- A summary of the changes and the motivation behind them.
- Steps to test or verify the changes.
- Whether the change affects the public API surface or downstream consumers.
-
Respond to review feedback. PRs may go through one or more rounds of review before being merged.
- Correctness of interceptor chains and handler mapping logic.
- Adherence to the builder pattern and
ClassBuilder<T>conventions. - Impact on downstream modules (
simplified-server). Breaking changes to the public API should be discussed in the issue tracker before implementation. - Security considerations (XSS in error pages, header injection, rate limit bypass).
- Compatibility with Spring Boot auto-configuration and consumer
scanBasePackagessetup.
Use GitHub Issues to report bugs or request features.
When reporting a bug, include:
- Java version (
java --version) - Spring Boot version (check
gradle/libs.versions.toml) - Operating system
- Full error stacktrace (if applicable)
- Steps to reproduce
- Expected vs. actual behavior
A brief overview to help you find your way around the codebase:
src/main/java/dev/sbs/serverapi/ # Framework library
├── config/
│ ├── ServerConfig.java # Immutable config with Builder, MemorySize,
│ │ # ShutdownMode, ForwardHeadersStrategy
│ └── ServerWebConfig.java # Security header interceptor + Gson message converters
├── error/
│ ├── ErrorController.java # Global @RestControllerAdvice with content negotiation
│ └── ErrorPageRenderer.java # Cloudflare-style HTML renderer with Placeholder enum
├── exception/
│ └── ServerException.java # Root exception with embedded HttpStatus
├── security/
│ ├── ApiKey.java # Key record with roles and sliding window counter
│ ├── ApiKeyAuthenticationInterceptor.java # HandlerInterceptor for auth/rate/perms
│ ├── ApiKeyConfig.java # Conditional bean registration
│ ├── ApiKeyProtected.java # @ApiKeyProtected annotation
│ ├── ApiKeyRole.java # Hierarchical role enum
│ ├── ApiKeyRoleHierarchy.java # Role expansion into reachable set
│ ├── ApiKeyService.java # Key storage, validation, rate limiting
│ ├── SecurityHeaderInterceptor.java # X-Content-Type-Options: nosniff
│ └── exception/ # MissingApiKeyException, InvalidApiKeyException,
│ # RateLimitExceededException, InsufficientPermissionException
└── version/
├── ApiVersion.java # @ApiVersion annotation
├── ApiVersionCondition.java # RequestCondition for version matching
├── ApiVersionConfig.java # Bean registration
├── ApiVersionHandlerMapping.java # Custom handler mapping with /v{N} prefixes
├── ApiVersionInterceptor.java # Defense-in-depth version validation
├── VersionRegistryService.java # Precomputed path-to-version index
└── exception/ # InvalidVersionException, MissingVersionException
src/test/java/dev/sbs/serverapi/ # Test harness
├── TestServer.java # Runnable test application (port 8080)
└── controller/
├── TestApiKeyController.java # API key auth test endpoints (/api/*)
└── TestVersionController.java # API versioning test endpoints (/v{N}/*)
- Custom error body - Override
ErrorController.buildErrorBody()to return a project-specific JSON error response type instead of the default map. - New security exception - Extend
SecurityExceptioninsecurity/exception/with the appropriateHttpStatus. - New version exception - Extend
VersionExceptioninversion/exception/with the appropriateHttpStatus. - Custom server config - Use
ServerConfig.builder()with any combination of settings, or start fromServerConfig.optimized()and adjust. - API key storage - Replace or extend
ApiKeyServiceto load keys from a database or external service instead of the current hardcoded test keys.
By submitting a pull request, you agree that your contributions are licensed under the Apache License 2.0, the same license that covers this project.