For people curious about how to use exposed dao's ...
# graphql-kotlin
d
For people curious about how to use exposed dao's here is a hacky way how to get the basics for queries only working:
Copy code
class ExposedDaoHook : SchemaGeneratorHooks {

    private val graphQLSizedIterable = GraphQLScalarType.newScalar()
        .name("SizedIterable")
        .description("A custom scalar that converts SizedIterable to List")
        .coercing(object : Coercing<SizedIterable<Any>, List<Any>> {
            override fun serialize(dataFetcherResult: Any, graphQLContext: GraphQLContext, locale: Locale): List<Any>? {
                return (dataFetcherResult as SizedIterable<Any>).toList()
            }
        })
        .build()

    private val graphQLLongEntityId = GraphQLScalarType.newScalar()
        .name("LongEntityID")
        .description("A custom scalar that converts LongEntityID to Long")
        .coercing(object : Coercing<EntityID<Long>, Long> {
            override fun serialize(dataFetcherResult: Any, graphQLContext: GraphQLContext, locale: Locale): Long {
                return (dataFetcherResult as EntityID<Long>).value
            }
        }).build()

    private val graphQLUUIDEntityID = GraphQLScalarType.newScalar()
        .name("UUIDEntityID")
        .description("A custom scalar that converts UUIDEntityID to UUID")
        .coercing(object : Coercing<EntityID<UUID>, UUID> {
            override fun serialize(dataFetcherResult: Any, graphQLContext: GraphQLContext, locale: Locale): UUID {
                return (dataFetcherResult as EntityID<UUID>).value
            }
        }).build()

    override fun willGenerateGraphQLType(type: KType): GraphQLType? {
        return when (type.classifier) {
            UUID::class -> ExtendedScalars.UUID
            Long::class -> ExtendedScalars.GraphQLLong
            EntityID::class ->
                when (type.arguments[0].type!!.classifier) {
                    Long::class -> graphQLLongEntityId
                    UUID::class -> graphQLUUIDEntityID
                    else -> null
                }

            Instant::class -> Scalars.GraphQLString
            LocalDate::class -> Scalars.GraphQLString
            LocalTime::class -> Scalars.GraphQLString
            Duration::class -> Scalars.GraphQLString
            SizedIterable::class -> graphQLSizedIterable
            else -> null
        }
    }


    private val excludedDaoProperties = listOf("klass", "db", "_readValues", "readValues", "writeValues")
    override fun isValidProperty(kClass: KClass<*>, property: KProperty<*>): Boolean {
        if (!kClass.allSuperclasses.contains(Entity::class))
            return super.isValidProperty(kClass, property)
        return !excludedDaoProperties.contains(property.name)
    }

    val excludedDaoMethods =
        listOf("isNewEntity", "delete", "flush", "refresh", "getValue", "setValue", "lookup", "storeWrittenValues")

    override fun isValidFunction(kClass: KClass<*>, function: KFunction<*>): Boolean {
        val superclasses = kClass.allSuperclasses
        if (!superclasses.contains(Entity::class))
            return super.isValidFunction(kClass, function)

        if (excludedDaoMethods.contains(function.name))
            return false
        println(function.name)
        return true
    }

    override fun willResolveMonad(type: KType): KType = when (type.classifier) {
        SizedIterable::class -> List::class.createType(type.arguments)
        else -> type
    }
}
This will make the types get serialized correctly. Now we can install the plugin:
Copy code
install(GraphQL) {
    schema {
        packages = listOf("com.example")
        queries = listOf(
            HelloQuery()
        )
        hooks = ExposedDaoHook()
    }
}

val graphQLPlugin = plugin(GraphQL)
routing {
        val route = post("graphql") {
            newSuspendedTransaction(<http://Dispatchers.IO|Dispatchers.IO>) {
                try {
                    graphQLPlugin.server.execute(call.request)?.let {
                        call.respond(it)
                    } ?: call.respond(HttpStatusCode.BadRequest)
                } catch (e: UnsupportedOperationException) {
                    call.respond(HttpStatusCode.MethodNotAllowed)
                } catch (e: Exception) {
                    call.respond(HttpStatusCode.BadRequest)
                }
            }
        }
        route.install(ContentNegotiation) {
            jackson()
        }
}
Sadly there is currently no way to wrap requests in a transaction. So we need to inline some internal functions of the library.
Copy code
class HelloQuery : Query {
    suspend fun user(id: Long) = UserDao.findById(id)
}

class Users : IdTable() {
    var forename = varchar("forename", 256)
    var surname = varchar("surname", 256)
}

class UserDao(id: EntityID<Long>) : LongEntity(id) {
    var forename by Users.forename
    var surname by Users.surname
}
Hope this helps someone that has the same use case, please feel free to ask or enhance my implementation if you see obvious bugs