Skip to content

Instantly share code, notes, and snippets.

@testfailed
Forked from felix19350/ktor-full-example.kt
Created May 20, 2022 05:20
Show Gist options
  • Save testfailed/8c3794417ebab6eb44c38592cb8389ee to your computer and use it in GitHub Desktop.
Save testfailed/8c3794417ebab6eb44c38592cb8389ee to your computer and use it in GitHub Desktop.
Barebones Ktor REST API example
package org.example
import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import io.ktor.application.call
import io.ktor.http.HttpStatusCode
import io.ktor.request.receive
import io.ktor.response.respond
import io.ktor.routing.Route
import io.ktor.routing.get
import io.ktor.routing.post
import kotlinx.coroutines.experimental.Dispatchers
import kotlinx.coroutines.experimental.withContext
import org.jetbrains.exposed.dao.EntityID
import org.jetbrains.exposed.dao.LongEntity
import org.jetbrains.exposed.dao.LongEntityClass
import org.jetbrains.exposed.dao.LongIdTable
import org.jetbrains.exposed.sql.Database
import org.jetbrains.exposed.sql.transactions.transaction
import javax.sql.DataSource
// Data model - data classes for input and output formats
data class MyAppUser(val email: String, val realName: String)
data class CreateMyAppUserCommand(val email: String, val realName: String)
// Core service definition
internal interface MyAppUserService {
suspend fun list(): List<MyAppUser>
suspend fun create(newUserCmd: CreateMyAppUserCommand): MyAppUser
}
// Data store connection setup shennanigans
fun quickNDirtyDb(): DataSource {
return HikariDataSource(HikariConfig().apply {
poolName = "HIKARI-POOL"
driverClassName = "org.h2.Driver"
jdbcUrl = "jdbc:h2:mem:test"
maximumPoolSize = 5
isAutoCommit = false
transactionIsolation = "TRANSACTION_READ_COMMITTED"
validate()
})
}
class DatastoreConnection(private val dataSource: DataSource) {
private val database: Database by lazy {
Database.connect(dataSource)
}
suspend fun <T> query(block: () -> T): T = withContext(Dispatchers.IO) {
transaction(database) {
block()
}
}
}
// Object relational mapping
internal object MyAppUserTable : LongIdTable("my_app_user_table") {
val email = varchar("user_email", 255).uniqueIndex()
val realName = varchar("real_name", 255)
}
internal class MyAppUserDAO(id: EntityID<Long>) : LongEntity(id) {
companion object : LongEntityClass<MyAppUserDAO>(MyAppUserTable)
var email by MyAppUserTable.email
var realName by MyAppUserTable.realName
fun toModel(): MyAppUser {
return MyAppUser(email, realName)
}
}
// Service implementation for datastore of choice
internal class MyAppUserServiceImpl(private val dbc: DatastoreConnection) : MyAppUserService {
override suspend fun list(): List<MyAppUser> {
return dbc.query {
MyAppUserDAO.all().map { it.toModel() }
}
}
override suspend fun create(newUserCmd: CreateMyAppUserCommand): MyAppUser {
return dbc.query {
MyAppUserDAO.new {
this.email = newUserCmd.email
this.realName = newUserCmd.realName
}.toModel()
}
}
}
//REST-ish API
fun Route.sampleApi() {
val service: MyAppUserService = MyAppUserServiceImpl(DatastoreConnection(quickNDirtyDb()))
get("/myUsers") {
call.respond(HttpStatusCode.OK, service.list())
}
post("/myUsers") {
//Don't forget to validate the input...
val newUser = service.create(call.receive())
call.respond(HttpStatusCode.OK, newUser)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment