Build an API with Kotlin on Google Cloud Platform

In this tutorial you will learn how to build a server side API using Kotlin and Ktor that you can host on Google Cloud Platform and use with your Android app. By Pablo Gonzalez Alonso.

4.2 (5) · 1 Review

Download materials
Save for later
Share

Google Cloud Platform has made building Kotlin-based server applications more accessible than ever. If you’ve always done mobile development, creating a server application in Kotlin for your Android app might look intimidating. Fear not, thanks to Google and Google Cloud Platform, you’ll be creating APIs in no time!

Picture this situation: the boss gives you an Android application for a conference. They also give you a Google Sheets document with the list of talks that you need to show, and ask you to create an API for the mobile app.

Since you’re a willing coder, you’ll of course take on this project with vim and vigor. As you build out this theoretical API, you’ll learn how to:

  • Configure Google Cloud Console and AppEngine
  • Create a server-side application, using Ktor and Kotlin
  • Access APIs for Google services, like Spreadsheets
  • Use data persistency using DataStore
  • Deploy to a scalable service
Note: This Google Cloud tutorial requires basic knowledge of Kotlin. If you are new to the language, you may find this video tutorial useful: Programming in Kotlin. You can also get up to speed with Ktor using our video course Server-Side Kotlin with Ktor.

Getting Started

Download the starter project from the link at the top or bottom of this tutorial. In there, you can find a module with an Android app, a common module for shared code, and a server module for the API. In this tutorial, you’ll be working on the server module.

Before moving into the code part, you need to set up Google Cloud and create a new project. If this is your first time working with it, make sure you follow these steps in detail. It can be a long process, so grab a drink and get comfortable. Fortunately, you only have to do this once! :]

Setting up Google Cloud

To manage Google Cloud, you need to have an internet browser and be signed in with a Google account. If you don’t have a Google account already, make sure to create one before continuing.

First, you need to create a new Google Cloud project. To do so navigate to the following URL:

If it’s your first time using Google Cloud, you’ll have to accept their Terms of Service and select a country.

Google cloud TOS

Once you’ve accepted the terms, you can name the project something meaningful. Name it “RayConf”:

Create new Google Cloud Project

Then click Create, which will forward you to the project’s dashboard. There you can see a summary of the resources used.

Now that you have a project, you need to create a new AppEngine Application. AppEngine is one of the many services that Google Cloud provides. It removes all the complications of creating a scalable service. You’ll be deploying the API there.

A project can have many Server Applications, but for this application, you only need one. Click the hamburger menu in the top-left corner, scroll down to the Compute section and click App Engine.

Select AppEngine

You will be taken to the AppEngine Dashboard view, with the below card already in place.

Create new App Engine Application

Select Create Application. Next, the setup process will ask you to select a region and zone. For this tutorial, you can use the defaults and select Create App.

Choose an App Engine region

Next, you need to select your language and environment. For the language, you have to select Java although you are going to use Kotlin, it will run on the Java Virtual Machine (JVM). You can leave the Environment as Standard and select Next.

Select your App Engine Language

You will see a confirmation message on the screen.

App Engine creation success

Now, follow the instructions to install by selecting the Download the SDK button. Once installed, you can verify that it works by running the following command in the terminal:

gcloud --version

You may need to restart the IDE for it to recognize the changes.

Google Cloud SDK version check

Brilliant, now that you have the SDK installed, you need to log in. To do so, run the following command in the terminal:

gcloud auth login

Log into Google Cloud Console

The browser will open and ask you to authorize the SDK with your Google account. Make sure this is the same account under which you created the AppEngine Project.

Authorize Google Cloud SDK

Once logged in, the project ID will be set to the only project you have on your account.

Look for “rayconf” in the list and copy the complete PROJECT_ID.

Note: If you have many projects, you need to first find the PROJECT_ID by running the following command:
gcloud projects list
gcloud projects list

Once you have the PROJECT_ID, you can switch to the project by running the following command:

gcloud config set project PROJECT_ID

Also, to make sure you have the latest version installed, run the following command:

gcloud components update

Now, you want to use the Google Sheets document your boss gave you to generate the API. Therefore, you have to permit AppEngine to access Sheets. To do so, you need first to enable the API here:

Enable Google Sheets API

Once the API is enabled, you need to create an account for the server to use. These accounts, called service accounts, are useful when there is no option for a user to log in, as is the case in the cloud. Go to the following URL to manage service accounts:

Note: Make sure the project is selected, if not then go ahead and select it.
Project Selection

Creating your First Endpoint

Defining a Server Endpoint for Talks

Create Service Account

Give it a meaningful name and select Create.

Enter Service Account details

Choose Editor as the role so you can edit resources, such as persistent data. No spoilers, but it’ll be handy later.

Add Editor role

Select Continue. On the next screen, you have the option to grant extra permissions and to create a key. It’s safe to ignore the first part, but you need to create a key so the server can authenticate itself.

Select Create key, make sure you have JSON ticked, and select the Create button.

Doing so generates the key and downloads a JSON file. This key is important as it grants your app access to resources, so make sure you keep it safe. To install it in the app, rename it to credential.json and place it in the following folder:

Save credential.json

The sample project has a convenience object named AppCredentials. It loads the key file into memory when needed.

I’m sure you will be glad to hear that this was the last thing you need to set up on Google Cloud.

Now it’s time to get to the fun part, actual code! :]

You are going to create an endpoint that returns the classic Hello World! message.

Start by adding a new Kotlin file to the server module and name it RayConfApp.kt:

Create RayConfApp.kt

Inside this file, create an extension function named main on Application class:

This function, once registered, is run when the server creates the Application. At which point, you will be:

You can now run the app by executing the following command in the terminal:

After some time the server will be accessible at the following URL:

Open it in a browser to see the result.

Check response in browser

The main function is registered with the application in the application.conf file:

ktor DSL routes

Ktor provides a great DSL (Domain Specific Language) for defining clear and descriptive servers. You’ve named the function main, but you can use any name and define different ones. This way, it’s easy to structure an application into separate parts for easy management.

It’s time to create something a bit more useful than a “Hello World!” example. The common module contains the model that the Android app is using. Since you are also writing the server application in Kotlin, you can use it in the server-side. Isn’t that efficient?

Start by adding another get("talks") block, right below the existing get("/") block inside RayConfApp.kt file:

Next, you need a list of events to return. For now, you can add it on the same file:

Now, you need to return a String that represents the contents of this list. For this, you need a way to convert the model into a String. Kotlin provides a simple way to do such serialization with kotlinx.serialization.

To set it up in the project, you will need to first add the plugin’s classpath. To do so navigate to build.gradle file at the root of the project. Then replace // Todo: Add kotlin-serialization plugin's classpath here with below:

Next, navigate to build.gradle file for the common module and apply the kotlin-serialization plugin by replacing // Todo: Add kotlin-serialization plugin here with below:

Lastly, add the kotlin-serialization dependency by replacing // Todo: Add kotlin-serialization dependency here with below:

All done, sync the project with Gradle to download all dependencies and configure plugins.

After adding the kotlinx serialization dependency to the project, adding the @Serializable annotation on AgendaEntry class is all that is needed:

  • https://console.developers.google.com/iam-admin/serviceaccounts and click on Create service account.
    import io.ktor.application.Application
    import io.ktor.application.call
    import io.ktor.response.respond
    import io.ktor.routing.get
    import io.ktor.routing.routing
    
    fun Application.main() {
        routing {                             // 1
            get("/") {                        // 2
                call.respond("Hello World!!") // 3
            }
        }
    }
    
    Note:You can learn more about extension functions here: Programming in Kotlin · Extension
    1. Defining a group of routes for the server
    2. Letting the server Application know that you want to handle a GET request on the root path
    3. Then, every time that request is made you respond to the call with a given response
    ./gradlew appengineRun
    
    Note: To stop the server, use the keyboard shortcut Control + C.
    Note: Always remember to update the application.conf when adding new ones.
    fun Application.main() {
      routing {
        get("/") {
          ...
        }
        get("talks") {
          // Add response here
        }
      }
    }
    
    import com.raywenderlich.common.AgendaEntry
    ...
    private val talks = listOf(
        AgendaEntry(
            id = 0,
            title = "This is the first talk",
            date = "28/09/2019",
            startTime = "09:00:00",
            endTime = "10:00:00",
            description = "Description for something very interesting, we hope.",
            speaker = "TBC"
        )
    )
    
    classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
    
    apply plugin: 'kotlinx-serialization'
    
    implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.13.0"
    
  1. Defining a group of routes for the server
  2. Letting the server Application know that you want to handle a GET request on the root path
  3. Then, every time that request is made you respond to the call with a given response
Note:You can learn more about extension functions here: Programming in Kotlin · Extension
Note: To stop the server, use the keyboard shortcut Control + C.
Note: Always remember to update the application.conf when adding new ones.
import io.ktor.application.Application
import io.ktor.application.call
import io.ktor.response.respond
import io.ktor.routing.get
import io.ktor.routing.routing

fun Application.main() {
    routing {                             // 1
        get("/") {                        // 2
            call.respond("Hello World!!") // 3
        }
    }
}
./gradlew appengineRun
fun Application.main() {
  routing {
    get("/") {
      ...
    }
    get("talks") {
      // Add response here
    }
  }
}
import com.raywenderlich.common.AgendaEntry
...
private val talks = listOf(
    AgendaEntry(
        id = 0,
        title = "This is the first talk",
        date = "28/09/2019",
        startTime = "09:00:00",
        endTime = "10:00:00",
        description = "Description for something very interesting, we hope.",
        speaker = "TBC"
    )
)
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
apply plugin: 'kotlinx-serialization'
implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.13.0"
import kotlinx.serialization.Serializable

@Serializable // <-- added
data class AgendaEntry(
    ...
)

Then, hide the fact that you’re using Kotlin serialization by adding parser functions inside the AgendaEntry.kt file:

import kotlinx.serialization.json.Json
import kotlinx.serialization.list

fun AgendaEntry.toJsonString(): String = 
  Json.stringify(AgendaEntry.serializer(), this)

fun List<AgendaEntry>.toJsonString(): String = 
  Json.stringify(AgendaEntry.serializer().list, this)

Hiding away your toJsonString() implementations like this is helpful for maintenance. It means that future changes to the serialization won't affect other parts of the code.

The first function uses the generated serializer, AgendaEntry.serializer(), to convert the entity into a string. The second one does the same but using the equivalent for lists: AgendaEntry.serializer().list. More information about this can be found in the official documentation here.

Now you can go back to the main function inside the RayConfApp.kt file. In there, add the code for the response, inside the get("talks") block, making sure all imports are resolved:

call.respondText(contentType = ContentType.Application.Json) {
  talks.toJsonString()
}

Here, you are telling Ktor:

  1. To respond with some text
  2. To set the content type to application/json
  3. And, finally, the lambda at the end returns a string which is a representation of the list of talks

Time to see it in action. Run the server again using ./gradlew appengineRun. Once deployed locally, verify in your browser that the result from http://localhost:8080/talks has a list with a single entry:

Check response in browser

Pablo Gonzalez Alonso

Contributors

Pablo Gonzalez Alonso

Author

Jason Donmoyer

Tech Editor

Chris Belanger

Editor

Julia Zinchenko

Illustrator

Nishant Srivastava

Final Pass Editor

Eric Soto

Team Lead

Over 300 content creators. Join our team.