Welcome! This project contains two complete REST API server examples built with Kotlin to help you transition from TypeScript/NestJS/Express to Kotlin/JVM ecosystem.
You'll find two fully-functional API servers:
- Ktor API Server - Lightweight, modern, async-first framework
- Spring Boot API Server - Enterprise-grade, feature-rich framework with MySQL integration
Both implement the same Users REST API for easy comparison.
kotlin-example/
βββ ktor-api-server/ # Ktor example (lightweight)
β βββ src/main/kotlin/
β β βββ Application.kt # All-in-one application file
β βββ build.gradle.kts
β βββ Dockerfile
β βββ README.md
β βββ gradlew # Gradle wrapper
β
βββ spring-boot-api-server/ # Spring Boot example (enterprise)
β βββ src/main/kotlin/com/example/
β β βββ Application.kt # Spring Boot main class
β β βββ controller/ # REST endpoints
β β βββ service/ # Business logic
β β βββ repository/ # Database access
β β βββ entity/ # Database models
β β βββ dto/ # API request/response objects
β β βββ exception/ # Error handling
β βββ src/main/resources/
β β βββ application.yaml # Default config
β β βββ application-dev.yaml # Dev config
β β βββ application-prod.yaml # Prod config
β βββ build.gradle.kts
β βββ Dockerfile
β βββ docker-compose.yml # MySQL container setup
β βββ init.sql # Database initialization
β βββ README.md
β βββ gradlew # Gradle wrapper
β
βββ README.md # This file
cd ktor-api-server
./gradlew run- API runs on:
http://localhost:8080 - In-memory data storage (no database needed)
- ~15 lines of main application code
- Perfect for learning Kotlin fundamentals
Step 1: Start MySQL
cd spring-boot-api-server
docker-compose up -dStep 2: Build and Run
./gradlew bootRun- API runs on:
http://localhost:8080 - MySQL database integration
- Swagger UI:
http://localhost:8080/swagger-ui.html - Full layered architecture (Controller β Service β Repository β Entity)
Both APIs implement the same Users REST API:
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/v1/users |
Get all users |
| GET | /api/v1/users/{id} |
Get specific user |
| POST | /api/v1/users |
Create new user |
| PUT | /api/v1/users/{id} |
Update user |
| DELETE | /api/v1/users/{id} |
Delete user |
| GET | /api/v1/health |
Health check |
Ktor:
curl http://localhost:8080/api/v1/usersSpring Boot:
curl http://localhost:8080/api/v1/usersSame endpoint, different implementation!
curl -X POST http://localhost:8080/api/v1/users \
-H "Content-Type: application/json" \
-d '{
"name": "Alice Johnson",
"email": "alice@example.com",
"age": 25
}'- Read
ktor-api-server/README.md - Run
./gradlew runand test endpoints - Study
Application.kt- understand the routing DSL - Learn Kotlin coroutines basics
- Compare with your Express.js experience
- Start MySQL with
docker-compose up -d - Read
spring-boot-api-server/README.md - Run
./gradlew bootRun - Explore Swagger UI at
/swagger-ui.html - Study the layered architecture (controller β service β repository)
- Learn Spring annotations (@RestController, @Service, @Repository, etc.)
- Compare with your NestJS experience
- Run both servers side-by-side
- Make the same API calls to both
- Study the differences in structure
- Modify code to add new endpoints
- Experiment with validation and error handling
Express.js: Ktor:
app.get('/') β get { }
app.post('/') β post { }
Middleware β Plugins/Install
req.body β call.receive()
res.json() β call.respond()
Promises/async β Coroutines
NestJS: Spring Boot:
@Controller() β @RestController
@Get() β @GetMapping()
@Injectable() β @Service
@Inject() β @Autowired
ValidationPipe β @Valid + validators
Global filter β @ControllerAdvice
- Framework: Ktor 2.3.6
- Language: Kotlin 1.9.21
- Runtime: Netty (async)
- Serialization: kotlinx.serialization
- Build: Gradle with Kotlin DSL
- JVM: Java 17+
- Framework: Spring Boot 3.1.6
- Language: Kotlin 1.9.21
- Database: MySQL 8.0
- ORM: Hibernate/JPA
- Validation: Jakarta Bean Validation
- Documentation: Springdoc OpenAPI (Swagger)
- Build: Gradle with Kotlin DSL
- JVM: Java 17+
-
Data Classes: Immutable objects with auto-generated methods
data class User(val id: Long, val name: String)
-
Coroutines: Lightweight threading for async programming
suspend fun fetchUser(): User { ... }
-
Extension Functions: Add methods to existing classes
fun String.isValidEmail(): Boolean { ... }
-
Sealed Classes: Type-safe error handling
sealed class Result<T> { data class Success<T>(val data: T) : Result<T>() data class Error<T>(val error: String) : Result<T>() }
-
Scope Functions: Clean object initialization
user.apply { name = "Updated" } .also { println(it) }
Get all users:
curl http://localhost:8080/api/v1/usersCreate user:
curl -X POST http://localhost:8080/api/v1/users \
-H "Content-Type: application/json" \
-d '{
"name": "Bob Smith",
"email": "bob@example.com",
"age": 30
}'Update user:
curl -X PUT http://localhost:8080/api/v1/users/1 \
-H "Content-Type: application/json" \
-d '{"name": "John Updated", "age": 31}'Delete user:
curl -X DELETE http://localhost:8080/api/v1/users/1Open browser: http://localhost:8080/swagger-ui.html
You can test all endpoints directly from the web interface!
// Application.kt contains:
- Data Models (@Serializable)
- Business Logic (UserStore)
- Routes (DSL style)Good for: Learning, microservices, prototypes
controller/ β HTTP layer (@RestController)
service/ β Business logic (@Service)
repository/ β Database access (Spring Data JPA)
entity/ β Database models (@Entity)
dto/ β API contracts (Request/Response)
exception/ β Error handling (@RestControllerAdvice)
Good for: Enterprise apps, complex logic, large teams
Ktor:
cd ktor-api-server
docker build -t ktor-users-api .
docker run -p 8080:8080 ktor-users-apiSpring Boot:
cd spring-boot-api-server
docker build -t spring-boot-users-api .
docker run -p 8080:8080 spring-boot-users-apicd spring-boot-api-server
docker-compose up -d # Start MySQL
docker-compose down # Stop MySQL./gradlew bootRun
# Uses: application.yaml
# Database: users_db
# Logging: INFO./gradlew bootRun --args='--spring.profiles.active=dev'
# Uses: application-dev.yaml
# Shows SQL queries
# More logging./gradlew bootRun --args='--spring.profiles.active=prod'
# Uses: application-prod.yaml
# Less logging
# Swagger disabledcd spring-boot-api-server
docker-compose up -dThis automatically:
- Starts MySQL 8.0
- Creates databases
- Loads sample data from init.sql
# If MySQL is already running locally
mysql -u root -p1234 < spring-boot-api-server/init.sql# Connect
mysql -u root -p1234
# View users
USE users_db;
SELECT * FROM users;
# Clear data
DELETE FROM users;- Data Classes: Perfect for DTOs and models
- Extension Functions: Can add methods to existing classes
- Coroutines: Learn async/await alternative
- Scope Functions:
apply,also,letfor clean code
- Routing DSL: Express-like syntax
- Plugins: Similar to middleware
- Serialization: Type-safe JSON handling
- Coroutines First: Everything is async
- Dependency Injection: Automatic wiring with annotations
- Repositories: JPA makes database queries simple
- Services: Business logic separation
- Controllers: HTTP request handling
- Exception Handlers: Centralized error handling
- Start Simple: Run Ktor first, it's simpler
- Compare Code: Look at both implementations
- Modify and Experiment: Change the code, see what happens
- Use Swagger: Test Spring Boot APIs through web UI
- Read Error Messages: Kotlin compiler is very helpful
- Check Logs: Spring Boot logs are detailed and informative
- Use Your IDE: IntelliJ IDEA has excellent Kotlin support
- Debug: Set breakpoints and step through code
- Document: Add comments to understand concepts
# Find process using port 8080
lsof -i :8080
# Kill the process
kill -9 <PID># Start MySQL
docker-compose up -d
# Verify it's running
docker ps | grep mysql# Clean and rebuild
./gradlew clean buildAfter completing this learning guide:
- Add new endpoints: Create endpoints for products, posts, etc.
- Add authentication: Implement JWT token validation
- Add pagination: Support limit/offset for list endpoints
- Add filtering: Filter users by name, age, email
- Add sorting: Sort results by different fields
- Add caching: Implement response caching
- Add monitoring: Add metrics and tracing
- Add tests: Write unit and integration tests
- Ktor Implementation: See
ktor-api-server/src/main/kotlin/Application.kt - Spring Boot Implementation: See files in
spring-boot-api-server/src/main/kotlin/com/example/ - Ktor Detailed Guide: See
ktor-api-server/README.md - Spring Boot Detailed Guide: See
spring-boot-api-server/README.md
cd ktor-api-server
./gradlew run # Start server
./gradlew build # Build project
./gradlew clean # Clean buildcd spring-boot-api-server
docker-compose up -d # Start MySQL
./gradlew bootRun # Start server
./gradlew build # Build project
./gradlew clean # Clean build
./gradlew bootRun --args='--spring.profiles.active=dev' # Dev modeThese examples are provided for learning purposes.
Happy Learning! π
Start with Ktor, then move to Spring Boot. Take your time to understand each framework before moving to the next one.