Home › Ktor (Kotlin) › Ktor server rules
Ktor server rules
Cursor rules for Ktor 3 servers in Kotlin: modules, plugins, routing, serialization, auth, StatusPages, and testApplication.
CursorKtor (Kotlin)
How to use
Save this as .cursor/rules/ktor.mdc in your repository root, commit it, and your agent will pick it up automatically. Edit the commands to match your project.
Note: Review and test before use. Provided as is, without warranty. See Terms.
.cursor/rules/ktor.mdcDownload ---
description: Ktor server conventions
globs: ["src/main/kotlin/**/*.kt","src/main/resources/application.yaml"]
alwaysApply: false
---
# Ktor 3 server
- Entry via `EngineMain` with `application.yaml`, or `embeddedServer(Netty, port = 8080, module = Application::module)`. Each feature is an extension `fun Application.configureOrders(service: OrderService)` called from `module()`.
- Install plugins once: `ContentNegotiation { json() }` (kotlinx.serialization), `StatusPages`, `CallLogging`, `Authentication`, `CORS` (explicit hosts), `RequestValidation`, `DefaultHeaders`.
- Routes grouped with `route("/orders") { get { } post { } }` in `routing { }`, split into `Route.orderRoutes()` extension functions.
- DTOs are `@Serializable data class`es. Read with `call.receive<CreateOrder>()`, validate (RequestValidation or explicit checks), respond with `call.respond(HttpStatusCode.Created, dto)`.
- Path params: `call.parameters["id"]?.toLongOrNull() ?: throw BadRequestException("bad id")`.
- Map exceptions in `StatusPages { exception<NotFoundException> { call, e -> call.respond(HttpStatusCode.NotFound, ErrorDto(e.message)) } }`; don't leak stack traces.
- Auth: `authenticate("jwt") { ... }` blocks around protected routes; get the user from `call.principal<JWTPrincipal>()`.
- Handlers are `suspend`; blocking DB calls go in `withContext(Dispatchers.IO)` or use a suspending driver (Exposed `suspendTransaction`, R2DBC).
- Config from `environment.config` / env vars; no hard-coded secrets.
## Done when
`./gradlew test` passes with `testApplication { application { module() }; client.get(...) }` tests for each route, and `./gradlew ktlintCheck` (or detekt) is clean.