A multiplayer Rock–Paper–Scissors API crafted with Ktor, offering JWT authentication, MongoDB persistence, live WebSocket play, leaderboard tracking, and more!
| Name | Description | |
|---|---|---|
| 🛣️ | Routing | Structured routing DSL for endpoint organization |
| 📡 | WebSockets | Real-time, bidirectional connections for live play |
| 🎛️ | Content Negotiation | Automatic (de)serialization (JSON and beyond!) |
| 📦 | kotlinx.serialization | Native, seamless JSON serialization/deserialization |
| 🔐 | Authentication | JWT-based endpoint security |
| 🔏 | Authorization | User role-based access protection |
| ❗ | Status Pages | Custom error/exception handling |
| 🌐 | CORS | Cross-Origin Resource Sharing for enabled endpoints |
| 🚦 | Rate Limiting | Request-throttling to fend off abuse |
| 🗄️ | MongoDB | Stores player info, history, and leaderboard |
| ⚙️ | Config YAML | Flexible app configuration via simple YAML |
| 📝 | Logging (Logback) | Deep insight and troubleshooting with advanced logs |
Here’s what’s next for RockPaperScissorsApi — PRs & suggestions welcome!
- Adding a database (MongoDB)
- JWT auth to secure endpoints
- Enhance features (game history, leaderboard)
- Logging and error handling
- Scaling with Redis (cashing) and Railway (auto-scaling)
- Security with rate-limiting and input validation
- Monitoring
- Add user registration endpoint
- Enhanced game session management
Support simultaneous games & reconnection logic - Retry/timeout logic for dropped WebSocket clients
- Admin endpoints for resetting leaderboard/games
- In-memory fallback for non-persistent testing
- API documentation (OpenAPI/Swagger) generation
- Rate limit per user (not only per IP)
- Unit and integration test improvements
- CLI client auto-completion & history
- Front-end client
nice-to-have! - Recording the results on blockchain
A very, very stretch goal - (Add your feature here — contributions encouraged!)
| Command | Action |
|---|---|
./gradlew test |
Run unit tests |
./gradlew build |
Build everything |
./gradlew :server:buildFatJar |
Build executable JAR (with dependencies) |
docker build -t rps-api . |
Build Docker image |
docker-compose up -d |
Start MongoDB in Docker |
./gradlew :server:run |
Run server locally |
docker run -p 8080:8080 rps-api |
Run the app in Docker |
- MongoDB Container
docker-compose up -d(port 27017)- Credentials in
docker-compose.yml
- Application Container
- Multi-stage build (Gradle 8.5 + JDK 17, OpenJDK 17 slim)
- Runs on port 8080
Authenticate player, get JWT
- Request:
{ "id": "", "name": "Ali" } - Response:
{ "token": "jwt-string", "playerId": "unique-id", "name": "Ali" } - 🔑 Use the returned token for subsequent endpoints.
Join a new game (JWT required)
- Headers:
Authorization: Bearer <jwt> - Request:
{ "id": "player-id", "name": "Ali" } - Responses:
201 Created{ "id": "unique-player-id", "name": "Ali" }409 ConflictGame is full
See all joined players
- Response:
[ { "id": "player1-id", "name": "Ali" }, { "id": "player2-id", "name": "Alya" } ]
Who’s winning? View wins per player
- Response:
[ { "player": "Ali", "wins": 7 }, { "player": "Alya", "wins": 5 }, { "player": "Tie", "wins": 2 } ]
Real-time RPS gameplay
- Send (client):
{ "playerId": "unique-player-id", "move": "ROCK" } - Receive (result):
{ "player1Id": "player1-id", "player2Id": "player2-id", "player1Move": "ROCK", "player2Move": "SCISSORS", "winner": "player1-id" } - 🔄 Play, see instant results, and rematch!
- Authenticate & Join: Log in, receive a JWT, and join a game securely.
- Discover: View current lobby players.
- Track: Leaderboard keeps score across games.
- Play Live: Send moves, get live results.
- Rematch Fast: Fresh match automatically starts after a game ends.
The CLI lets you:
- Authenticate, join games
- List players, play interactively
Tech:
- Clikt (v5.0.3) — CLI parser
- Mordant (v3.0.2) — Terminal colors/markdown
- Ktor Client — HTTP/WS API communication
- Kotlin
2.1.10 - Ktor
3.1.2 - MongoDB
7 - JWT Authentication
- Docker & Docker Compose
- Gradle
8.5 - JDK
17 - Logback
1.4.14
🥳 Enjoy Rock-Paper-Scissors powered by Kotlin!
Questions, feedback & PRs welcome!