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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Build an API with Kotlin on Google Cloud Platform
30 mins
- Getting Started
- Setting up Google Cloud
- Creating a Google Cloud Project
- Creating an AppEngine Application
- Installing Google Cloud
- Logging into Google Cloud
- Enabling Google Sheets API
- Authorizing Service Account
- Deploying to Google Cloud
- Adding Dynamic Data with Google Drive
- Reading Data From a Spreadsheet
- Combining Headers and Rows
- Building Agenda Entries
- Putting the Transformations Together
- Integrating the SpreadSheetDataSource
- Caching Response
- Mapping Entities
- Creating a Local Data Source
- Composing Data Sources
- Integrating the Cache
- Implementing Votes
- Updating LocalDataStore to save votes
- Creating the Voting Endpoint
- Storing Votes After Updates
- Where to Go From Here?
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
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.
Once you’ve accepted the terms, you can name the project something meaningful. Name it “RayConf”:
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.
You will be taken to the AppEngine Dashboard view, with the below card already in place.
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.
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.
You will see a confirmation message on the screen.
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.
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
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.
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.
gcloud projects list
Look for “rayconf” in the list and copy the complete PROJECT_ID.
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:
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:
Creating your First Endpoint
Defining a Server Endpoint for Talks
Give it a meaningful name and select Create.
Choose Editor as the role so you can edit resources, such as persistent data. No spoilers, but it’ll be handy later.
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:
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:
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.
The main function is registered with the application in the application.conf file:
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.
Give it a meaningful name and select Create.
Choose Editor as the role so you can edit resources, such as persistent data. No spoilers, but it’ll be handy later.
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:
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! :]
Creating your First Endpoint
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:
Inside this file, create an extension function named
main
onApplication
class: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 · ExtensionThis function, once registered, is run when the server creates the Application. At which point, you will be:
- Defining a group of routes for the server
- Letting the server Application know that you want to handle a GET request on the root path
- Then, every time that request is made you respond to the call with a given response
You can now run the app by executing the following command in the terminal:
./gradlew appengineRun
After some time the server will be accessible at the following URL:
Open it in a browser to see the result.
Note: To stop the server, use the keyboard shortcut Control + C.The main function is registered with the application in the application.conf file:
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.Note: Always remember to update theapplication.conf
when adding new ones.Defining a Server Endpoint for Talks
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 existingget("/")
block inside RayConfApp.kt file:fun Application.main() { routing { get("/") { ... } get("talks") { // Add response here } } }
Next, you need a list of events to return. For now, you can add it on the same file:
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" ) )
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:classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
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:apply plugin: 'kotlinx-serialization'
Lastly, add the kotlin-serialization dependency by replacing
// Todo: Add kotlin-serialization dependency here
with below:implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.13.0"
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 onAgendaEntry
class is all that is needed:
- Defining a group of routes for the server
- Letting the server Application know that you want to handle a GET request on the root path
- Then, every time that request is made you respond to the call with a given response
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:
- To respond with some text
- To set the content type to
application/json
- 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: