Cursor Rules
kotlin ktor development - Claude MCP Skill
Instruction to developer: save this file as .cursorrules and place it on the root project directory
SEO Guide: Enhance your AI agent with the kotlin ktor development tool. This Model Context Protocol (MCP) server allows Claude Desktop and other LLMs to instruction to developer: save this file as .cursorrules and place it on the root project directory... Download and configure this skill to unlock new capabilities for your AI workflow.
Documentation
SKILL.md## Instruction to developer: save this file as .cursorrules and place it on the root project directory
## Core Principles
- Follow **SOLID**, **DRY**, **KISS**, and **YAGNI** principles
- Adhere to **OWASP** security best practices
- Break tasks into smallest units and solve problems step-by-step
## Technology Stack
- **Framework**: Kotlin Ktor with Kotlin 2.1.20+
- **JDK**: 21 (LTS)
- **Build**: Gradle with Kotlin DSL
- **Dependencies**: Ktor Server Core/Netty, kotlinx.serialization, Exposed, HikariCP, kotlin-logging, Koin, Kotest
## Application Structure (Feature-Based)
- **Organize by business features, not technical layers**
- Each feature is self-contained with all related components
- Promotes modularity, reusability, and better team collaboration
- Makes codebase easier to navigate and maintain
- Enables parallel development on different features
```
src/main/kotlin/com/company/app/
āāā common/ # Shared utilities, extensions
āāā config/ # Application configuration, DI
āāā features/
āāā auth/ # Feature directory
ā āāā models/
ā āāā repositories/
ā āāā services/
ā āāā routes/
āāā users/ # Another feature
āāā ...
```
Test structure mirrors the feature-based organization:
```
src/test/kotlin/com/company/app/
āāā common/
āāā features/
āāā auth/
ā āāā models/
ā āāā repositories/
ā āāā services/
ā āāā routes/
āāā users/
āāā ...
```
## Application Logic Design
1. Route handlers: Handle requests/responses only
2. Services: Contain business logic, call repositories
3. Repositories: Handle database operations
4. Entity classes: Data classes for database models
5. DTOs: Data transfer between layers
## Entities & Data Classes
- Use Kotlin data classes with proper validation
- Define Table objects when using Exposed ORM
- Use UUID or auto-incrementing integers for IDs
## Repository Pattern
```kotlin
interface UserRepository {
suspend fun findById(id: UUID): UserDTO?
suspend fun create(user: CreateUserRequest): UserDTO
suspend fun update(id: UUID, user: UpdateUserRequest): UserDTO?
suspend fun delete(id: UUID): Boolean
}
class UserRepositoryImpl : UserRepository {
override suspend fun findById(id: UUID): UserDTO? = withContext(Dispatchers.IO) {
transaction {
Users.select { Users.id eq id }
.mapNotNull { it.toUserDTO() }
.singleOrNull()
}
}
// Other implementations...
}
```
## Service Layer
```kotlin
interface UserService {
suspend fun getUserById(id: UUID): UserDTO
suspend fun createUser(request: CreateUserRequest): UserDTO
suspend fun updateUser(id: UUID, request: UpdateUserRequest): UserDTO
suspend fun deleteUser(id: UUID)
}
class UserServiceImpl(
private val userRepository: UserRepository
) : UserService {
override suspend fun getUserById(id: UUID): UserDTO {
return userRepository.findById(id) ?: throw ResourceNotFoundException("User", id.toString())
}
// Other implementations...
}
```
## Route Handlers
```kotlin
fun Application.configureUserRoutes(userService: UserService) {
routing {
route("/api/users") {
get("/{id}") {
val id = call.parameters["id"]?.let { UUID.fromString(it) }
?: throw ValidationException("Invalid ID format")
val user = userService.getUserById(id)
call.respond(ApiResponse("SUCCESS", "User retrieved", user))
}
// Other routes...
}
}
}
```
## Error Handling
```kotlin
open class ApplicationException(
message: String,
val statusCode: HttpStatusCode = HttpStatusCode.InternalServerError
) : RuntimeException(message)
class ResourceNotFoundException(resource: String, id: String) :
ApplicationException("$resource with ID $id not found", HttpStatusCode.NotFound)
fun Application.configureExceptions() {
install(StatusPages) {
exception<ResourceNotFoundException> { call, cause ->
call.respond(cause.statusCode, ApiResponse("ERROR", cause.message ?: "Resource not found"))
}
exception<Throwable> { call, cause ->
call.respond(HttpStatusCode.InternalServerError, ApiResponse("ERROR", "An internal error occurred"))
}
}
}
```
## Testing Strategies and Coverage Requirements
### Test Coverage Requirements
- **Minimum coverage**: 80% overall code coverage required
- **Critical components**: 90%+ coverage for repositories, services, and validation
- **Test all edge cases**: Empty collections, null values, boundary conditions
- **Test failure paths**: Exception handling, validation errors, timeouts
- **All public APIs**: Must have integration tests
- **Performance-critical paths**: Must have benchmarking tests
### Unit Testing with Kotest
```kotlin
class UserServiceTest : DescribeSpec({
describe("UserService") {
val mockRepository = mockk<UserRepository>()
val userService = UserServiceImpl(mockRepository)
it("should return user when exists") {
val userId = UUID.randomUUID()
val user = UserDTO(userId.toString(), "Test User", "test@example.com")
coEvery { mockRepository.findById(userId) } returns user
val result = runBlocking { userService.getUserById(userId) }
result shouldBe user
}
it("should throw exception when user not found") {
val userId = UUID.randomUUID()
coEvery { mockRepository.findById(userId) } returns null
shouldThrow<ResourceNotFoundException> {
runBlocking { userService.getUserById(userId) }
}
}
}
})
```
## Route Testing with Ktor 3.x
```kotlin
class UserRoutesTest : FunSpec({
test("GET /api/users/{id} returns 200 when user exists") {
val mockService = mockk<UserService>()
val userId = UUID.randomUUID()
val user = UserDTO(userId.toString(), "Test User", "test@example.com")
coEvery { mockService.getUserById(userId) } returns user
testApplication {
application {
configureRouting()
configureDI { single { mockService } }
}
client.get("/api/users/$userId").apply {
status shouldBe HttpStatusCode.OK
bodyAsText().let {
Json.decodeFromString<ApiResponse<UserDTO>>(it)
}.data shouldBe user
}
}
}
})
```
## Key Principles for Testable Code
1. **Single Responsibility**: Each method should do one thing well
2. **Pure Functions**: Same input always produces same output
3. **Dependency Injection**: Constructor injection for testable components
4. **Clear Boundaries**: Well-defined inputs and outputs
5. **Small Methods**: Extract complex logic into testable helper functions
## Configuration Management
```kotlin
// Type-safe configuration
interface AppConfig {
val database: DatabaseConfig
val security: SecurityConfig
}
data class DatabaseConfig(
val driver: String,
val url: String,
val user: String,
val password: String
)
// Access in application
fun Application.configureDI() {
val appConfig = HoconAppConfig(environment.config)
install(Koin) {
modules(module {
single<AppConfig> { appConfig }
single { appConfig.database }
})
}
}
```
## Security Best Practices
```kotlin
fun Application.configureSecurity() {
install(Authentication) {
jwt("auth-jwt") {
// JWT configuration
}
}
install(DefaultHeaders) {
header(HttpHeaders.XContentTypeOptions, "nosniff")
header(HttpHeaders.XFrameOptions, "DENY")
header(HttpHeaders.ContentSecurityPolicy, "default-src 'self'")
header("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
}
}
```
## Health Checks & Monitoring
```kotlin
fun Application.configureMonitoring() {
val startTime = System.currentTimeMillis()
routing {
get("/health") {
call.respond(mapOf("status" to "UP", "uptime" to "${(System.currentTimeMillis() - startTime) / 1000}s"))
}
get("/metrics") {
call.respond(prometheusRegistry.scrape())
}
}
install(MicrometerMetrics) {
registry = PrometheusMeterRegistry(PrometheusConfig.DEFAULT)
meterBinders = listOf(
JvmMemoryMetrics(),
JvmGcMetrics(),
ProcessorMetrics(),
JvmThreadMetrics()
)
}
}
```
## Performance Tuning
- **JVM Settings**: `-XX:+UseG1GC -XX:MaxGCPauseMillis=100 -XX:MaxRAMPercentage=75.0`
- **Connection Pooling**: Configure HikariCP with proper sizing based on workload
- **Caching**: Use Caffeine for in-memory caching of frequently accessed data
- **Coroutines**: Use structured concurrency for asynchronous processing
- **Database Queries**: Optimize with proper indexing, batch operations, paginationSignals
Information
- Repository
- PatrickJS/awesome-cursorrules
- Author
- PatrickJS
- Last Sync
- 3/12/2026
- Repo Updated
- 3/12/2026
- Created
- 1/14/2026
Reviews (0)
No reviews yet. Be the first to review this skill!
Related Skills
upgrade-nodejs
Upgrading Bun's Self-Reported Node.js Version
cursorrules
CrewAI Development Rules
cn-check
Install and run the Continue CLI (`cn`) to execute AI agent checks on local code changes. Use when asked to "run checks", "lint with AI", "review my changes with cn", or set up Continue CI locally.
CLAUDE
CLAUDE.md
Related Guides
Bear Notes Claude Skill: Your AI-Powered Note-Taking Assistant
Learn how to use the bear-notes Claude skill. Complete guide with installation instructions and examples.
Mastering tmux with Claude: A Complete Guide to the tmux Claude Skill
Learn how to use the tmux Claude skill. Complete guide with installation instructions and examples.
OpenAI Whisper API Claude Skill: Complete Guide to AI-Powered Audio Transcription
Learn how to use the openai-whisper-api Claude skill. Complete guide with installation instructions and examples.