Введение
Kotlin — современный язык программирования, который стремительно набирает популярность в бэкенд-разработке. Благодаря лаконичному синтаксису, полной совместимости с Java и мощным инструментам, таким как фреймворк Ktor, написание REST API на Kotlin занимает буквально 30 минут. В этой статье мы создадим полноценный REST API для управления списком задач (Todo API). Вы научитесь настраивать проект, создавать эндпоинты, подключать базу данных и тестировать API. Никакой лишней теории — только практика.
Шаг 1: Настройка проекта и зависимости
Для быстрого старта используем IntelliJ IDEA (Community Edition) или любой другой редактор. Создадим новый проект Gradle с Kotlin DSL.
Создание структуры проекта
Добавьте в файл build.gradle.kts следующие зависимости:
plugins {
kotlin("jvm") version "1.9.22"
application
}
repositories {
mavenCentral()
}
dependencies {
implementation("io.ktor:ktor-server-core:2.3.7")
implementation("io.ktor:ktor-server-netty:2.3.7")
implementation("io.ktor:ktor-server-content-negotiation:2.3.7")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.7")
implementation("org.jetbrains.exposed:exposed-core:0.44.1")
implementation("org.jetbrains.exposed:exposed-dao:0.44.1")
implementation("org.jetbrains.exposed:exposed-jdbc:0.44.1")
implementation("com.h2database:h2:2.2.224")
implementation("ch.qos.logback:logback-classic:1.4.14")
}
application {
mainClass.set("ApplicationKt")
}
Мы используем:
- Ktor — легковесный асинхронный фреймворк для HTTP-серверов.
- Exposed — ORM для работы с базами данных.
- H2 — встроенная база данных для разработки.
- Kotlinx.serialization — для работы с JSON.
Шаг 2: Создание модели данных и подключение БД
Определим структуру таблицы задач. Создайте файл Database.kt:
import org.jetbrains.exposed.sql.Table
import org.jetbrains.exposed.sql.insert
import org.jetbrains.exposed.sql.selectAll
import org.jetbrains.exposed.sql.transactions.transaction
object Tasks : Table() {
val id = integer("id").autoIncrement()
val title = varchar("title", 255)
val completed = bool("completed").default(false)
override val primaryKey = PrimaryKey(id)
}
data class Task(val id: Int, val title: String, val completed: Boolean)
fun initDatabase() {
Database.connect("jdbc:h2:mem:test", driver = "org.h2.Driver")
transaction {
SchemaUtils.create(Tasks)
}
}
Этот код создаёт таблицу Tasks с тремя полями: id, title и completed. Функция initDatabase() инициализирует базу данных в памяти.
Шаг 3: Написание контроллеров REST API
Теперь создадим основной файл приложения Application.kt. Здесь мы определим все эндпоинты.
Базовая настройка сервера
import io.ktor.server.application.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.routing.*
import io.ktor.server.response.*
import io.ktor.server.request.*
import io.ktor.serialization.kotlinx.json.*
import io.ktor.server.plugins.contentnegotiation.*
fun main() {
embeddedServer(Netty, port = 8080) {
install(ContentNegotiation) {
json()
}
initDatabase()
configureRouting()
}.start(wait = true)
}
Реализация эндпоинтов CRUD
fun Application.configureRouting() {
routing {
// Получить все задачи
get("/tasks") {
val tasks = transaction {
Tasks.selectAll().map { row ->
Task(
id = row[Tasks.id],
title = row[Tasks.title],
completed = row[Tasks.completed]
)
}
}
call.respond(tasks)
}
// Создать задачу
post("/tasks") {
val task = call.receive<Task>()
val id = transaction {
Tasks.insert {
it[title] = task.title
it[completed] = task.completed
} get Tasks.id
}
call.respond(mapOf("id" to id, "status" to "created"))
}
// Обновить задачу
put("/tasks/{id}") {
val id = call.parameters["id"]?.toIntOrNull()
if (id == null) {
call.respondText("Invalid ID", status = io.ktor.http.HttpStatusCode.BadRequest)
return@put
}
val updatedTask = call.receive<Task>()
transaction {
Tasks.update({ Tasks.id eq id }) {
it[title] = updatedTask.title
it[completed] = updatedTask.completed
}
}
call.respond(mapOf("status" to "updated"))
}
// Удалить задачу
delete("/tasks/{id}") {
val id = call.parameters["id"]?.toIntOrNull()
if (id == null) {
call.respondText("Invalid ID", status = io.ktor.http.HttpStatusCode.BadRequest)
return@delete
}
transaction {
Tasks.deleteWhere { Tasks.id eq id }
}
call.respond(mapOf("status" to "deleted"))
}
}
}
Мы создали четыре эндпоинта:
GET /tasks— получение списка всех задач.POST /tasks— создание новой задачи.PUT /tasks/{id}— обновление существующей задачи.DELETE /tasks/{id}— удаление задачи.
Шаг 4: Запуск и тестирование API
Запустите приложение. Сервер стартует на порту 8080. Для тестирования используйте Postman, curl или браузер.
Примеры запросов
- GET
http://localhost:8080/tasks— вернёт пустой массив[]. - POST
http://localhost:8080/tasksс телом{"title": "Изучить Kotlin", "completed": false}— создаст задачу. - PUT