Using Cloud Pub/Sub on Node.js from Kotlin/JS

Megan Potter
Google Cloud - Community
5 min readJun 1, 2021

--

Why Kotlin/JS?

Kotlin is commonly used for front-end JVM work, but as shown in the previous post about Kotlin and the JVM, Kotlin can work quite well on the server side as well. In order to maintain a full Kotlin stack in a web application, Kotlin/JS was developed.

Kotlin/JS is basically a standard Kotlin compiler, but instead of writing to JVM bytecode, it transpiles* into JavaScript. It serves much the same purpose in this situation as TypeScript, providing type safety and syntactic sugar to some common operations. Like TypeScript, it also provides a concept of external typings for JavaScript libraries. Using the Dukat utility, one may also convert TypeScript typings into Kotlin typings, making it very easy to get full type safety in a Kotlin/JS application.

* Transpilation is like compilation, but converting from one human-readable language to another, rather than binary code.

With Kotlin on both the client and server side, generally using React and a JVM, respectively, it’s possible to share some code directly, like generic business logic and data classes. It’s also possible to leverage existing competencies in Kotlin for a team developing a vertical application stack.

Kotlin/JS … on Node?

Yes, it’s true! Because Kotlin/JS is simply a transpiler, it’s also possible to run the resulting code using the Node.js runtime. This gives you the ability to build very lightweight server processes using all of the existing Node compatible libraries from npm/yarn, while staying in Kotlin for the whole application.

Caveats

This article will walk you through building a Kotlin/JS application that runs in Node.js, and uses the Node.js Pub/Sub libraries for communicating with Google Cloud Pub/Sub. Please note that we do not recommend doing this for your production work at this time; this article is mostly for the curious!

Project setup

Begin by installing a recent version of Node.js. You may also install it using nvm, but you’ll need to set the path to npm in the project settings below.

Open either IntelliJ IDEA or Android Studio and create a Kotlin/JS project targeted at Node.js. As of this time, the new IR compiler is still experimental, so I recommend using LEGACY to keep things simple. (By the time you read this, that may not be the case.)

To work with the Pub/Sub libraries, we must add them to our Gradle build configuration. Edit the build.gradle.kts file, and add the following line:

As the comment suggests, you could normally enable generateExternals, and it would invoke Dukat for you to automatically convert any TypeScript typings in @google-cloud/pubsub. Unfortunately, it is currently failing, so we’ll make our own very simple typings!

Kotlin/JS Typings

In order to call the Pub/Sub library in a type-safe way, we need to add some typings. I like to create a file with the same naming convention as TypeScript, such as pubsub.d.kt, but your tastes may vary.

Begin the file pubsub.d.kt like this:

This tells the compiler several things:

  • This file contains typings for the @google-cloud/pubsub library
  • The typings may be used outside of a Kotlin/JS module setting
  • The items described should have a Kotlin namespace of google.cloud.pubsub

Our types will use JavaScript Promise<> objects, so we also need to import those typings:

Finally, the actual typings. You can find the full example here, but for example, for the PubSub class:

I also like to make a Kotlin-idiomatic data class for JSON blobs we’d normally pass in. These can’t be in the same file — you must make a separate non-external file for that.

In JavaScript, this will turn into a standard { projectId: null, apiEndpoint: null } object.

Interfacing with Pub/Sub

We can now call the Node.js Pub/Sub library in a more or less normal way. This example uses the emulator:

This is pretty nice! The subClient.on() call has turned into a Kotlin operator like “use”.

Kotlin/JS and Coroutines

Let’s take a look at another operation that might be common:

Unfortunately, this returns a Promise<>, not the value we want. You can interface with this in the normal JavaScript way:

And again, this looks pretty good thanks to the Kotlin lambda parameter syntax. But we can do better.

In TypeScript, you’d simply use async and await. Kotlin/JS doesn’t currently support JavaScript native async and await. It does, however, provide Kotlin coroutines! And it turns out that it’s not too hard to bridge the two. That discussion is from a while ago, and coroutines have since come out of the experimental phase, so it requires a little bit of work to bring it up to date.

First, we need an adapter between JavaScript Promise<> and Kotlin coroutines. This extension function will let you call .await() on any Promise<>, and the call becomes a suspend function call:

And since Kotlin/JS isn’t running on the JVM, we can’t use JVM-native operations like runBlocking. So this implements a bridge from non-suspend code to a suspend function, kind of like calling an async function in JavaScript without await:

And now we can do this sort of thing:

Hey, that’s looking pretty good, actually! We’re back to the ease of use of TypeScript async and await, but we are also writing Kotlin. This will transpile down gracefully into an application you can run on Node.js with no JVM.

Next steps

Check out a full working example that runs on the Pub/Sub emulator.

Have you used Kotlin/JS on the server side with Node.js? Do you find this interesting and wish to hear more? Please feel free to leave a comment about this post or things that you find interesting/promising in regards to Kotlin and Kotlin/JS on Google Cloud Platform!

--

--

Megan Potter
Google Cloud - Community

Software Engineer at Google, for Google Cloud Platform, in Ontario, Canada