A modern, full-stack URL shortener application built with Kotlin. Features a beautiful React-based frontend and a robust Kotlin/JVM backend with real-time communication via Ktor and kotlinx RPC.
- URL Shortening: Quickly convert long URLs into short, shareable links
- Base64 URL-Safe Encoding: Efficient 6-character shortened URLs using Base64Url encoding
- Real-time API: WebSocket-based communication between frontend and backend using kotlinx RPC
- Beautiful UI: Modern, responsive React interface with smooth animations
- Database Persistence: SQLite database for storing URL mappings
- Comprehensive Testing: Full test coverage with kotlin.test for both backend and common modules
- Cross-Platform: Kotlin Multiplatform project (JVM backend, JS frontend, Common shared API)
Shorty/
βββ common/ # Shared Kotlin code
β βββ src/commonMain # Shared API definitions
β β βββ ShortId.kt # Base64 encoding/decoding utilities
β β βββ ShortyApi.kt # RPC interface definition
β βββ src/commonTest # Shared tests
β
βββ server/ # Kotlin/JVM Backend
β βββ src/main/kotlin
β β βββ Application.kt # Server entry point
β β βββ de/shorty/service/
β β β βββ ShortyImpl.kt # Core service implementation
β β β βββ model/ShortsTable.kt # Database table definition
β β βββ de/shorty/api/
β βββ src/test/kotlin
β βββ ShortyImplTest.kt # 17 comprehensive test cases
β
βββ frontend/ # Kotlin/JS Frontend
β βββ src/jsMain/kotlin
β β βββ App.kt # Main application component
β β βββ Main.kt # Entry point
β β βββ RPC.kt # RPC client initialization
β β βββ components/
β β βββ Logo.kt # Logo component
β β βββ ShortLinkForm.kt # URL input form
β β βββ ShortLinkResult.kt # Result display
β β βββ ErrorMessage.kt # Error handling
β βββ src/jsMain/resources
β βββ shorty_logo.png # Application logo
β
βββ build.gradle.kts # Root build configuration
- Kotlin/JS: Kotlin compiled to JavaScript
- React 19: Modern UI framework
- Emotion CSS: Styled components solution
- Ktor Client: HTTP/WebSocket communication
- kotlinx RPC: Type-safe RPC over WebSocket
- Kotlin/JVM: Backend server logic
- Ktor Server: HTTP server and WebSocket support
- Exposed: SQL framework for database operations
- SQLite: Embedded SQL database
- kotlinx RPC: Type-safe RPC support
- kotlinx Coroutines: Async/await support
- kotlin.test: Lightweight testing framework
- JUnit Platform: Test runner
@Rpc
interface ShortyApi {
suspend fun shortenUrl(url: Url): ShortId
suspend fun resolveShortId(shortId: ShortId): Url?
suspend fun getAllShortIds(startIndex: Long = 0, count: Int = 100): List<Pair<ShortId, Url>>
suspend fun deleteShortId(shortId: ShortId): Boolean
}Shortened URLs follow the pattern:
http://localhost:8080/s/{base64EncodedId}
Where {base64EncodedId} is a URL-safe Base64 encoded 32-bit integer (6 characters).
encodeShortIdToBase64(shortId: ShortId): String- Encode a ShortId to Base64UrldecodeShortIdFromBase64(encoded: String): ShortId- Decode Base64Url to ShortId
This project uses Detekt for Kotlin static analysis. Configuration lives in config/detekt/detekt.yml.
Run Detekt:
./gradlew detektRun Detekt for a specific module:
./gradlew server:detekt
./gradlew common:detekt
./gradlew frontend:detektReports are generated in build/reports/detekt/.
Common Module Tests:
./gradlew common:jvmTestServer Module Tests:
./gradlew server:test- 16 Server Tests: CRUD operations, edge cases, special characters, pagination
- 6 Common Tests: Base64 encoding/decoding, round-trip validation, error handling
- 100% Pass Rate: All tests use kotlin.test annotations with camelCase naming
- Java 11+
- Gradle 7.0+
- Node.js (for frontend assets)
The frontend runs on port 3000 with hot-reload support:
./gradlew frontend:jsBrowserDevelopmentRunThen open http://localhost:3000 in your browser. API requests are routed to http://localhost:8080.
Option 1: Run server only (without frontend)
Simply run the main function in server/src/main/kotlin/Application.kt from your IDE.
Option 2: Run server with latest frontend
This command compiles the production frontend and bundles it with the server:
./gradlew server:runAppNote: This is slower due to production webpack compilation, but uses the latest frontend code.
The server will start on http://localhost:8080.
- User enters a long URL in the frontend input field
- Frontend calls
shortenUrl()via RPC WebSocket - Backend generates a random ShortId and stores the mapping
- Frontend receives the ShortId and encodes it to Base64Url
- Shortened URL is displayed:
http://localhost:8080/s/{encodedId} - User can copy the shortened URL to clipboard
- Anyone can access the short link and be redirected to the original URL
- Enter URL
- Shortened Result
- Defines the
ShortyApiRPC interface - Contains
ShortIdtype (32-bit Int) - Provides Base64Url encoding/decoding utilities
- Compiled to both JVM and JS targets
- Implements
ShortyApiinterface - Manages SQLite database with Exposed
- Handles concurrent requests with coroutines
- Provides REST/RPC endpoints
- Displays beautiful UI with Emotion styling
- Manages local state with React hooks
- Communicates with backend via RPC
- Lazy loads and displays results
- Base64Url Encoding: Uses URL-safe alphabet (
-and_instead of+and/) - Input Validation: All URLs are validated before storing
- Database Isolation: Each test uses an isolated in-memory database
- Error Handling: Graceful error messages for invalid inputs
- Encoding/Decoding: O(1) time complexity for Base64Url operations
- Database: SQLite with optimized queries and indexing
- API Response: Sub-100ms for typical URL shortening
- Frontend: Production webpack bundle ~1MB (minified)
This project demonstrates best practices for:
- Kotlin Multiplatform development
- Type-safe RPC communication
- React/Kotlin/JS integration
- Comprehensive testing with camelCase naming
- Component-based UI architecture
Copyright 2023-2024 Jochen Guck, Use of this source code is governed by the Apache 2.0 license.
- DESIGN_DECISIONS.md - Comprehensive design decisions document explaining:
- Why 32-bit Integer with collision detection instead of UUID (22 chars vs 6 chars)
- Base64Url encoding strategy
- Database choice (SQLite)
- RPC framework selection (kotlinx RPC)
- Component architecture
- Testing strategy
- Multiplatform module design
- Error handling approach
- And more architectural insights
- Github actions (Automated tests, Dependabot)
- Docker image generation (automated)
- Integration Tests (User Stories)
- Analytics dashboard for URL statistics
- URL expiration/TTL support
- QR code generation
- User authentication and URL management


