considering the following javascript code (partially taken from Apollo Server documentation), it creates an instance of ApolloServer and start it.
const {ApolloServer} = require('apollo-server')
const server = new ApolloServer({ ... });
server.listen().then(({ url }) => {
console.log(`Server ready at ${url}`);
});
Now consider to replicate the same behaviour using KotlinJS.
Firstly, Kotlin doesn't have the "new" keyword and calling ApolloServer()
as expected, won't work but raise an error (TypeError: Class constructor ApolloServer cannot be invoked without 'new').
// We can banally represent part of the code above like:
external fun require(module: String): dynamic
val ApolloServer = require("apollo-server").ApolloServer
// ApolloServer is a js class
Declaring an external class like:
external open class ApolloServer() {
open fun listen(vararg opts: Any): Promise<Any>
operator fun invoke(): Any
}
and set it as ApolloServer type doesn't help.
How do we replicate "new ApolloServer()" call?
To solve this problem I found an interesting approach based on JsModule annotation. We need to create a Kotlin file that represent that javascript module we want to import, in my case "apollo-server".
@file:JsModule("apollo-server")
@file:JsNonModule
package com.package
import kotlin.js.Promise
external interface ServerInfo {
var address: String
var family: String
var url: String
var subscriptionsUrl: String
var port: dynamic /* Number | String */
get() = definedExternally
set(value) = definedExternally
var subscriptionsPath: String
var server: Any
}
external open class ApolloServer(config: Any? /* ApolloServerExpressConfig & `T$0` */) : Any {
open var httpServer: Any
open var cors: Any
open var onHealthCheck: Any
open var createServerInfo: Any
open fun applyMiddleware()
open fun listen(vararg opts: Any): Promise<ServerInfo>
open fun stop(): Promise<Unit>
}
With the above code we are basically describing what we expect to find in the apollo-server module and how to map it into Kotlin.
In our Kotlin main function we don't have to specify any require(...) but just use our ApolloServer class like:
ApolloServer(null).listen().then {
console.log(it)
}
Using this approach Kotlin would transpile it correctly, using the new keyword in javascript.
Transpiled version extract:
function main$lambda(it) {
console.log(it);
return Unit;
}
function main() {
(new ApolloServer(null)).listen().then(main$lambda);
}
This code is just an example, ApolloServer won't be initialized without a proper configuration, this case, for example, contains a nullable configuration.