Ktor es un framework de servidor web ligero y rápido para Kotlin, desarrollado por JetBrains.
Es ideal para crear aplicaciones web y servicios RESTful, y es muy fácil de usar y configurar.
En este artículo, vamos a ver cómo crear un backend para una aplicación de notas utilizando Ktor.
Crear el proyecto de Ktor
Para crearlo, lo más sencillo es ir a start.ktor.io, y crear un proyecto.
Elige el plugin de Content Negotiation.
Una vez finalices el proceso, se descargará un zip que necesitas descomprimir.
Abre la carpeta descomprimida desde IntelliJ.
Crea la clase de datos
Lo primero es el modelo del servidor. Para ello vamos a crear una clase anotada con @Serializable
, para que la librería de content negotiation se capaz de convertirla a Json y viceversa.
@Serializable data class Note(val id: Int, val title: String, val content: String)
Asegúrate de que tienes el plugin de Kotlin Serialization
correctamente configurado.
plugins { ... kotlin("plugin.serialization") version "1.7.20" }
Primer endpoint
Ahora podemos crear nuestra primera ruta en nuestro backend. En Ktor, las rutas son simplemente funciones que manejan solicitudes HTTP. Vamos a crear una ruta que maneje solicitudes GET a la ruta /notes
y devuelva una lista de notas en formato JSON. Para hacer esto, vamos a crear un objeto NotesRepository
con una función getAll
:
object NotesRepository { fun getAll(): List<Note> { val notes = (0..10).map { Note( "Title $it", "Description $it", if (it % 3 == 0) Type.AUDIO else Type.TEXT ) } return notes } }
Ahora tenemos que enlazar esta función a una ruta en nuestro servidor Ktor.
Para ello, vamos a crear una clase Application
y añadir un método configureRouting
que contenga nuestras rutas:
class Application { fun Application.configureRouting() { route("/notes") { get { call.respond(NotesRepository.getAll()) } } } }
Configura la serialización
Vamos a crear otra función configureSerialization()
que identifica cómo se va a hacer la conversión entre el objeto y el resultado, que será en Json:
fun Application.configureSerialization() { install(ContentNegotiation) { json(Json { prettyPrint = true }) } }
Crear el servidor Ktor
Por último, necesitamos crear una instancia de nuestra clase Application
y arrancar el servidor Ktor. Para ello, vamos a crear una función main
en nuestro archivo Main.kt
:
fun main() { embeddedServer(Netty, port = 8080, host = "0.0.0.0") { configureRouting() configureSerialization() }.start(wait = true) }
Con esto, ya tenemos nuestro servidor Ktor en funcionamiento y nuestra ruta /notes
lista para recibir solicitudes GET. Si abrimos nuestro navegador y vamos a la dirección http://localhost:8080/notes
, deberíamos ver el listado de notas en formato JSON.
Configurar el resto de endpoints
Para crear un servidor sencillo, podemos almacenar las notas en memoria, guardadas en una lista, y configurar todos los endpoints basados en esto:
Create
Permite añadir nuevas notas. Devolverá un error en el caso en que la serialización falle:
post { try { val note = call.receive<Note>() val savedNote = NotesRepository.save(note) call.respond(HttpStatusCode.Created, savedNote) } catch (e: Exception) { call.respond( HttpStatusCode.BadRequest, "Bad JSON Data Body: ${e.message.toString()}" ) } }
Read
Ya teníamos una función que devolvía todas las notas, a la que vamos a añadir otra que devuelva una por id:
get { call.respond(NotesRepository.getAll()) } get("{id}") { val id = call.parameters["id"] ?: return@get call.respond( HttpStatusCode.BadRequest, "Missing or malformed id" ) val note = NotesRepository.getById(id.toLong()) ?: return@get call.respond( HttpStatusCode.NotFound, "No note with id $id" ) call.respond(note) }
Update
Actualiza el contenido de una nota. También lanza un error en el caso de que la nota no pueda serializarse correctamente desde el Json.
Además, lanzará otro error si la nota a actualizar no existe:
put { try { val note = call.receive<Note>() if (NotesRepository.update(note)) { call.respond(note) } else { call.respond( HttpStatusCode.NotFound, "No note with id ${note.id}" ) } } catch (e: Exception) { call.respond( HttpStatusCode.BadRequest, "Bad JSON Data Body: ${e.message.toString()}" ) } }
Delete
Borra una nota. Lanzará errores si no consigue parsear el identificador de la nota, o si la nota no existe:
delete("{id}") { val id = call.parameters["id"] ?: return@delete call.respond( HttpStatusCode.BadRequest, "Missing or malformed id" ) if (NotesRepository.delete(id.toLong())) { call.respond(HttpStatusCode.Accepted) } else { call.respond( HttpStatusCode.NotFound, "No order with id $id" ) } }
Repositorio completo
Aquí te dejo el repositorio de notas con todas las operaciones. Sería interesante en un proyecto real que los datos se persistieran en una base de datos, pero este punto lo dejo para otro artículo:
object NotesRepository { private val list = mutableListOf<Note>() private var currentId = 1L fun save(note: Note): Note = note.copy(id = currentId++) .apply(list::add) fun getAll(): List<Note> = list fun getById(id: Long): Note? = list.find { it.id == id } fun update(note: Note): Boolean = list.indexOfFirst { it.id == note.id } .takeIf { it >= 0 } ?.also { list[it] = note } .let { it != null } fun delete(id: Long): Boolean = list.indexOfFirst { it.id == id } .takeIf { it >= 0 } ?.also { list.removeAt(it) } .let { it != null } }
Conclusión
En conclusión, Ktor es un marco de servidor ligero y fácil de usar para crear aplicaciones web y servicios REST en Kotlin.
Con su soporte para una amplia variedad de bases de datos y su facilidad de configuración y uso, Ktor es una excelente opción para desarrolladores que buscan una forma rápida y sencilla de crear un servidor para sus aplicaciones.
Además, gracias a su integración con otros frameworks y herramientas populares, como Hibernate y Exposed, Ktor es altamente personalizable y se puede adaptar a una amplia variedad de usos y necesidades.
En resumen, si estás buscando una forma sencilla de crear un servidor en Kotlin, Ktor es una excelente opción a tener en cuenta.
0 comentarios