A Spring Boot implementation of the RealWorld API specification.
Built with modern Java 25, Spring Boot 4, and GraalVM native image support for instant startup and minimal resource footprint.
A complete implementation of the RealWorld API spec on Spring Boot 4 and Java 25, built and shipped as a GraalVM native image alongside the usual JVM jar. Every release is checked against the upstream spec in CI on both, so "complete" is machine-verified rather than asserted, and it works with any RealWorld frontend.
What it demonstrates: JWT authentication, a CQRS-inspired command and query split, Spring Data JDBC, schema versioning with Liquibase, layered testing, and native binaries for four platforms produced on every release.
What it does not: the demo runs H2 in memory on a free instance, so read this as a worked reference rather than a production deployment.
Check out the live application on Render:
| Resource | URL |
|---|---|
| api | https://realworld-backend-spring.onrender.com/api |
| swagger-ui | https://realworld-backend-spring.onrender.com/swagger-ui/index.html |
💡 The application is deployed on a free tier, so it may take a few seconds to start.
Modern Spring Stack:
- Spring Boot 4
- Spring Data JDBC for lightweight data access
- Spring Security OAuth2 Resource Server with JWT authentication
- Spring Boot Actuator for monitoring and management
- Springdoc OpenAPI for interactive API documentation
Application Design:
- Multi-module Gradle build with Kotlin DSL
- Command/Query Bus pattern for CQRS-inspired separation of concerns
- Database aggregate views for optimized read operations
- Modern Java features: records, sealed interfaces, pattern matching
- MapStruct with Spring integration for type-safe DTO mapping
- JSpecify nullability annotations for enhanced type safety
Data & Infrastructure:
- Liquibase for versioned database schema management
- H2 in-memory database for development and testing
- Spring Declarative HTTP Clients for integration tests
Native Compilation:
- Full GraalVM native image support with optimized runtime hints
- Multi-platform builds: Linux (AMD64/ARM64), macOS (ARM64), Windows (AMD64)
- Native Docker images for linux/amd64 and linux/arm64, on a minimal Wolfi base for production deployment
The project features a comprehensive automated pipeline:
- Multi-platform builds: JVM JAR + native executables for four platforms
- Automated testing with JUnit and integration tests
- RealWorld spec compliance verification via the upstream hurl collection
- Code coverage tracking with Codecov
- Multi-arch Docker image building and publishing to GitHub Container Registry
- Automated GitHub releases with platform-specific artifacts
- Continuous deployment to Render
Requirements: Java 25
Choose your preferred way to run the application:
Traditional Spring Boot application with the full JVM:
./gradlew bootRunOr download the JAR from the releases page:
java -jar realworld-backend-spring-*.jarGraalVM native executable for instant startup and minimal memory footprint:
./gradlew nativeRunOr download the pre-built native executable for your platform from the releases page:
./realworld-backend-springAvailable platforms: Linux (AMD64/ARM64), macOS (ARM64), Windows (AMD64)
Run the containerized native image, published for linux/amd64 and linux/arm64:
docker run -p 8080:8080 ghcr.io/alexey-lapin/realworld-backend-spring:latestThe application will be available at:
- API: http://localhost:8080/api
- Swagger UI: http://localhost:8080/swagger-ui/index.html
This backend implements the complete RealWorld API specification and works seamlessly with any RealWorld frontend.
API Base URL: http://localhost:8080/api
Point your frontend to this endpoint and you're ready to go. All authentication, CRUD operations, pagination, and filtering are fully supported.
Three layers of tests: unit tests, integration tests, and compliance against the RealWorld spec itself.
# Unit tests
./gradlew test
# Integration tests, which are a separate suite and do not run as part of `test`
./gradlew integrationTest
# Both suites plus Spotless, the same verification the CI build runs
./gradlew check
# Verify against the RealWorld API spec (needs https://hurl.dev installed).
# The spec lives upstream; check it out into .realworld-spec, the same path CI
# uses. The revision is read from the workflow rather than repeated here, so a
# local run and a CI run cannot drift onto different specs.
SPEC_REF=$(grep -E '^ SPEC_REF:' .github/workflows/main.yml | awk '{print $2}')
# First time only:
git clone --filter=blob:none --sparse https://github.com/realworld-apps/realworld .realworld-spec
git -C .realworld-spec sparse-checkout set specs/api
# Before each run (also refreshes an existing checkout):
git -C .realworld-spec fetch origin && git -C .realworld-spec checkout --detach "$SPEC_REF"
./gradlew build
java -jar service/build/libs/realworld-backend-spring*.jar &
hurl --test --jobs 1 \
--variable host=http://localhost:8080 \
--variable uid=$(date +%s) \
.realworld-spec/specs/api/hurl/*.hurl.realworld-spec is a foreign clone with its own .git, so it is gitignored
and excluded from the Docker build context. It is about 2 MB.
Two things worth knowing:
hostis the origin without/api. The.hurlfiles append the prefix themselves, and upstream's own README example gets this wrong.- Check out
SPEC_REF, don't just run whatever the checkout happens to be on. The spec is a moving target and its requirements change: duplicate article titles once had to return409, and now must be accepted with distinct slugs. An old checkout still enforces rules the spec has since dropped, so tests fail against a service that is correct. To move to a newer spec, bumpSPEC_REFin.github/workflows/main.ymldeliberately and fix whatever it turns red.
A Bruno collection generated from the same files is available upstream if you prefer to explore the requests interactively; the hurl files are the source of truth and are what CI runs.
Test suite includes:
- Unit tests for business logic and handlers
- Integration tests using Spring's declarative HTTP clients
- RealWorld API spec validation with hurl
- Code coverage reporting via JaCoCo and Codecov
For interactive API exploration and testing, use the included IntelliJ IDEA HTTP Client file:
File: api.http
This file contains ready-to-use requests for all API endpoints:
- User registration and authentication
- Profile management (follow/unfollow)
- Article CRUD operations with feed and filtering
- Comments and favorites
- Tags listing
Open the file in IntelliJ IDEA or any compatible IDE and execute requests directly. The file uses environment variables and response handlers to chain requests automatically (e.g., capturing tokens for authenticated requests).
Load test the application with the included k6 script:
k6 run k6-create-articles.jsThe script simulates realistic article creation workload:
- 5 virtual users for 30 seconds
- Performance thresholds: <1% error rate, p95 latency <750ms
- Automatic user registration and JWT token management
- Random tag generation for realistic data distribution
Customize the test by setting the BASE_URL environment variable:
k6 run -e BASE_URL=https://your-api.com/api k6-create-articles.js