Как написать REST API на Kotlin за 30 минут: на практике

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Как написать REST API на Kotlin за 30 минут: на практике

Kotlin — современный язык программирования, который стремительно набирает популярность в backend-разработке. Благодаря лаконичному синтаксису, полной совместимости с Java и мощным инструментам, таким как фреймворк Ktor, создание REST API становится быстрым и приятным процессом. В этой статье мы на практике покажем, как написать простой, но полноценный REST API на Kotlin буквально за полчаса. Вы узнаете, как настроить проект, создать эндпоинты, обрабатывать JSON и запустить сервер.

Статья предназначена для разработчиков, знакомых с основами Kotlin, но не требует глубоких знаний backend-разработки. Весь код будет сопровождаться подробными пояснениями.

1. Настройка проекта и добавление зависимостей

Для начала нам потребуется создать новый проект Kotlin. Мы будем использовать Gradle — самую популярную систему сборки для Kotlin. Если вы используете IntelliJ IDEA, выберите «New Project» -> «Kotlin» -> «JVM | IDEA». В противном случае создайте папку проекта вручную.

Первым делом откройте файл build.gradle.kts и добавьте следующие зависимости для Ktor и сериализации JSON:

plugins {
    kotlin("jvm") version "1.9.22"
    kotlin("plugin.serialization") 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("ch.qos.logback:logback-classic:1.4.14")
}

application {
    mainClass.set("ApplicationKt")
}

После синхронизации Gradle мы готовы к написанию кода. Обратите внимание, что мы используем плагин kotlinx.serialization для работы с JSON — это стандартный способ в Kotlin.

2. Создание модели данных и основного файла сервера

Для REST API нам нужна модель данных. Пусть это будет простой список задач (ToDo). Создайте файл Todo.kt:

import kotlinx.serialization.Serializable

@Serializable
data class Todo(
    val id: Int,
    val title: String,
    val completed: Boolean = false
)

Теперь создадим основной файл Application.kt (или Main.kt). В нем мы настроим сервер и запустим его:

import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.application.*
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, host = "0.0.0.0") {
        configureSerialization()
        configureRouting()
    }.start(wait = true)
}

fun Application.configureSerialization() {
    install(ContentNegotiation) {
        json()
    }
}

fun Application.configureRouting() {
    routing {
        get("/") {
            call.respondText("Hello, REST API on Kotlin!")
        }
    }
}

Этот код запускает сервер на порту 8080. Мы подключили модуль ContentNegotiation для автоматической сериализации/десериализации JSON. Запустите приложение — вы должны увидеть в браузере по адресу http://localhost:8080 приветственное сообщение.

3. Реализация CRUD-эндпоинтов (Create, Read, Update, Delete)

Теперь давайте добавим полноценные REST маршруты для управления задачами. Мы будем использовать список mutableListOf для хранения данных в памяти (в реальном проекте вы замените его на базу данных).

Создадим файл TodoRepository.kt:

import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

class TodoRepository {
    private val todos = mutableListOf(
        Todo(1, "Изучить Kotlin"),
        Todo(2, "Написать REST API")
    )
    private var nextId = 3
    private val mutex = Mutex()

    suspend fun getAll(): List<Todo> = mutex.withLock { todos.toList() }

    suspend fun getById(id: Int): Todo? = mutex.withLock { todos.find { it.id == id } }

    suspend fun create(todo: Todo): Todo = mutex.withLock {
        val newTodo = todo.copy(id = nextId++)
        todos.add(newTodo)
        newTodo
    }

    suspend fun update(id: Int, todo: Todo): Todo? = mutex.withLock {
        val index = todos.indexOfFirst { it.id == id }
        if (index != -1) {
            val updated = todo.copy(id = id)
            todos[index] = updated
            updated
        } else null
    }

    suspend fun delete(id: Int): Boolean = mutex.withLock {
        todos.removeAll { it.id == id }
    }
}

Обратите внимание на использование Mutex — это обеспечивает потокобезопасность, так как Ktor работает с корутинами.

Теперь обновим функцию configureRouting() в Application.kt:

fun Application.configureRouting() {
    val repository = TodoRepository()

    routing {
        get("/todos") {
            val todos = repository.getAll()
            call.respond(todos)
        }

        get("/todos/{id}") {
            val id = call.parameters["id"]?.toIntOrNull() ?: return@get call.respondText(
                "Invalid id", status = io.ktor.http.HttpStatusCode.BadRequest
            )
            val todo = repository.getById(id)
            if (todo != null) {
                call.respond(todo)
            } else {
                call.respondText("Not Found", status = io.ktor.http.HttpStatusCode.NotFound)
            }
        }

        post("/todos") {
            val todo = call.receive<Todo>()
            val created = repository.create(todo)
            call.respond(io.ktor.http.HttpStatusCode.Created, created)
        }

        put("/todos/{id}") {
            val id = call.parameters["id"]?.toIntOrNull() ?: return@put call.respondText(
                "Invalid id", status = io.ktor.http.HttpStatusCode.BadRequest
            )
            val todo = call.receive<Todo>()
            val updated = repository.update(id, todo)
            if (updated != null) {
                call.respond(updated)
            } else {
                call.respondText("Not Found", status = io.ktor.http.HttpStatusCode.NotFound)
            }
        }

        delete("/todos/{id}") {
            val id = call.parameters["id"]?.toIntOrNull() ?: return@delete call.respondText(
                "Invalid id", status = io.ktor                

Блоги

Рекомендаций по книгам