Introduction to Appwrite Cloud Functions with Android and Kotlin

Student @ National Institute of Technology, Delhi.
Android | Kotlin | Go | Python
Search for a command to run...

Student @ National Institute of Technology, Delhi.
Android | Kotlin | Go | Python
No comments yet. Be the first to comment.
Failures are Inevitable Network calls might time out, APIs might be temporarily unavailable, or databases might become unreachable. To handle such transient errors gracefully, implementing a retry mechanism is crucial. This article will guide you thr...

In the last article, we covered data classes in Kotlin. We saw how they simplify class creation and make it faster with less coding. Now, let's explore Sealed Classes. Sealed Classes in Kotlin are a specialized form of abstract classes. They limit su...

Efficient Data Structuring in Android Development with Kotlin's Data Classes

Learn about Kotlin type hierarchy, type checks and type casts

Understanding and Utilizing Polymorphism and Inheritance in Kotlin for Effective Development

In this article, we'll take a look at Cloud Functions in appwrite.io and how to use them with Kotlin or Android.
Here's an overview of what we'll be doing.
Before we start, if you like to dive directly into the code, here's the github repo
Before we dive into creating functions, let's set up a new Appwrite project to which we will add our functions. If you already have an appwrite project you can skip this section.
To get Appwrite up and running, follow these instructions and create a new account.
Next, follow the onscreen instructions to create a new project -

This is a pretty basic project to implement but it will make us familiar with the process of creating a new Appwrite Function. Let's get started -
First of all, we need a new Kotlin project, I'm going to use Intellij Idea as an IDE with gradle build system.
Here's the configuration I'm using to create a new project -

Next, let's create a new file named Main.kt in hello-world/src/main/kotlin and put the following code in it -
fun main() {
println("Hello, world!")
}
...and we are done!
Appwrite requires us to create a "fat jar" which is just a fancy way of saying that we need to include all runtime dependencies in our jar file. To achieve this we have to add the following lines to build.gradle.kts -
tasks.withType<Jar>() {
manifest {
attributes["Main-Class"] = "MainKt"
}
from(sourceSets.main.get().output)
dependsOn(configurations.runtimeClasspath)
from({
configurations.runtimeClasspath.get()
.filter { it.name.endsWith("jar") }
.map { zipTree(it) }
})
}
These lines basically instruct Gradle to build a "fat jar". Now to actually build a jar, we need to run the following task -

This will create the required jar file in hello-world/build/libs/hello-world-1.0-SNAPSHOT.jar
Now, to upload this to Appwrite we need to package this in a tar file. To do this run the following command inside the libs directory -
tar -zcvf code.tar.gz .\hello-world-1.0-SNAPSHOT.jar
This will create a file named code.tar.gz in the libs directory. We will need this file later.
Now, let's create a new appwrite function from the appwrite console. Navigate to Functions tab on Left pane, and click on Add Function button. Give it a name, in this case I'm using hello-world, and for the runtime, choose Java 16.0
Next, let's create a new deploy tag -

Here's the Command -
java -jar hello-world-1.0-SNAPSHOT.jar
Here we upload the tar file we created earlier. Click Activate and voila, we're done. :D
Let's test out the function. Click on Execute Now we don't need to pass any data to this function. Navigate to logs and check the output

And you just created your first appwrite function. Cheers 🍻
Now let's make this function a little more useful. Instead of printing Hello, world! all the time, we'll pass a name to this function.
APPWRITE_FUNCTION_DATA and APPWRITE_FUNCTION_EVENT_DATAAs mentioned in the documentation, these environment variables contains the information pertinent to execution of a custom appwrite function.
We use APPWRITE_FUNCTION_DATA when we trigger the function through Appwrite console or via SDK or HTTP API. This variable contains the data passed in those executions.
APPWRITE_FUNCTION_EVENT_DATA is used when the function is triggered by some event like inserting a new document. This variable contains information regarding that event.
Now we need to read the name from the APPWRITE_FUNCTION_DATA variable, to do this we add a new function -
fun getNameFromEnv(): String =
System.getenv("APPWRITE_FUNCTION_DATA")
and update the println statement as follows -
println("Hello, ${getNameFromEnv()}!")
Let's build the jar and add a new deploy tag to our appwrite console. You can follow the same instructions from above. And then execute the function passing you name as input.
Let's test it out.

Yay! We're done. 🚀
By creating the previous project, we got familiar with the process of creating a new Appwrite function with Kotlin. Now, let's build another project which is a little more complex. In this project, we'll see how we can integrate an Appwrite Function with a third-party API. Let's get started.
We will be using Covid19Api to get the data.
Before we dive into implementing the function, let's take a quick look at the requirements of what we'll be building -
APPWRITE_FUNCTION_DATA.Let's first create a new project. I'll name it get-covid-stats. Then we need a Main.kt file in get-covid-stats/src/main/kotlin.
Now we need to read the country name from APPWRITE_FUNCTION_DATA. Let's do that -
fun readCountryFromEnv(): String =
System.getenv("APPWRITE_FUNCTION_DATA")
and in main let's call it -
suspend fun main() {
val country = readCountryFromEnv()
}
Next, we need to install some dependencies. We need to make Network Requests for that, I'm going to use Ktor HTTP Client and to parse json let's use kotlinx.serialization. If you don't know these libraries, don't worry, I'll explain how they work. To install these dependencies add the following lines to build.gradle.kts -
dependencies {
// ....
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.0")
implementation("io.ktor:ktor-client-core:1.6.4")
implementation("io.ktor:ktor-client-cio:1.6.4")
implementation("io.ktor:ktor-client-serialization:1.6.4")
}
There is one more line we need to add for kotlinx.serialization to work properly. You can read more about it here. Let's add the following line to build.gradle.kts -
plugins {
// ...
kotlin("plugin.serialization") version "1.5.31"
}
Next, we need some classes to hold responses we receive from Covid19Api. We'll save the classes in model package. Let's create them -
First, let's create an interface with data we need to return in get-covid-stats/src/main/kotlin/model/ICovidStats.kt
package model
interface ICovidStats {
val newConfirmed: Int
val totalConfirmed: Int
val newDeaths: Int
val totalDeaths: Int
val newRecovered: Int
val totalRecovered: Int
}
Now, if we take a look at data returned from the endpoint we see we need three classes Response.kt, GlobalStats.kt and CountryStats.kt. Let's create them -
// GlobalStats.kt
package model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class GlobalStats(
@SerialName("NewConfirmed") override val newConfirmed: Int,
@SerialName("TotalConfirmed") override val totalConfirmed: Int,
@SerialName("NewDeaths") override val newDeaths: Int,
@SerialName("TotalDeaths") override val totalDeaths: Int,
@SerialName("NewRecovered") override val newRecovered: Int,
@SerialName("TotalRecovered") override val totalRecovered: Int,
) : ICovidStats
@Serializable tells kotlinx.serialization that this class can be parsed to/from JSON and @SerialName is used to indicate the JSON field name.
// CountryStats.kt
package model
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class CountryStats(
@SerialName("Country") val country: String,
@SerialName("CountryCode") val countryCode: String,
@SerialName("Slug") val slug: String,
@SerialName("NewConfirmed") override val newConfirmed: Int,
@SerialName("TotalConfirmed") override val totalConfirmed: Int,
@SerialName("NewDeaths") override val newDeaths: Int,
@SerialName("TotalDeaths") override val totalDeaths: Int,
@SerialName("NewRecovered") override val newRecovered: Int,
@SerialName("TotalRecovered") override val totalRecovered: Int,
) : ICovidStats
// Response.kt
package model
import kotlinx.serialization.Serializable
import kotlinx.serialization.SerialName
@Serializable
data class Response(
@SerialName("Global") val global : GlobalStats,
@SerialName("Countries") val countries : List<CountryStats>,
)
Okay 🤯, these are the response models we need.
We also need an object which we will return from this function. Let's do that -
// FunctionResult.kt
package model
import kotlinx.serialization.Serializable
@Serializable
data class FunctionResult(
val isGlobal: Boolean,
val newConfirmed: Int,
val totalConfirmed: Int,
val newDeaths: Int,
val totalDeaths: Int,
val newRecovered: Int,
val totalRecovered: Int,
)
We will also need a JSON Parser, Let's set up kotlinx.serialization json Parser -
val jsonParser = Json {
isLenient = true
ignoreUnknownKeys = true
}
Next, let's get the stats from the API. First, we need an HTTP Client to make the requests. Let's do that -
HttpClient() {
install(JsonFeature) {
serializer = KotlinxSerializer(json = jsonParser)
}
}
Here, we also install JsonFeature which automatically parses the response to classes we created earlier. Now, let's use this client to make a get request to https://api.covid19api.com/summary.
HttpClient() {
// ...
}.use { client ->
val response: Response = client.get("https://api.covid19api.com/summary")
}
Now let's get the country or global data from this and create a FunctionResult object -
val result: FunctionResult = response.countries.find {
it.country.equals(country, ignoreCase = true) ||
it.countryCode.equals(country, ignoreCase = true) ||
it.slug.equals(country, ignoreCase = true)
}?.run {
FunctionResult(
false, newConfirmed, totalConfirmed, newDeaths,
totalDeaths, newRecovered, totalRecovered
)
} ?: response.global.run {
FunctionResult(
true, newConfirmed, totalConfirmed, newDeaths,
totalDeaths, newRecovered, totalRecovered
)
}
Let's put this information in stdout -
println(jsonParser.encodeToString(result))
Here's the complete code -
import io.ktor.client.*
import io.ktor.client.features.json.*
import io.ktor.client.features.json.serializer.*
import io.ktor.client.request.*
import io.ktor.utils.io.core.*
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import model.FunctionResult
import model.Response
suspend fun main() {
val country = readCountryFromEnv()
val jsonParser = Json {
isLenient = true
ignoreUnknownKeys = true
}
HttpClient() {
install(JsonFeature) {
serializer = KotlinxSerializer(json = jsonParser)
}
}.use { client ->
val response: Response = client.get("https://api.covid19api.com/summary")
val result: FunctionResult = response.countries.find {
it.country.equals(country, ignoreCase = true) ||
it.countryCode.equals(country, ignoreCase = true) ||
it.slug.equals(country, ignoreCase = true)
}?.run {
FunctionResult(
false, newConfirmed, totalConfirmed, newDeaths,
totalDeaths, newRecovered, totalRecovered
)
} ?: response.global.run {
FunctionResult(
true, newConfirmed, totalConfirmed, newDeaths,
totalDeaths, newRecovered, totalRecovered
)
}
println(jsonParser.encodeToString(result))
}
}
fun readCountryFromEnv(): String =
System.getenv("APPWRITE_FUNCTION_DATA")
Whew, that was a lot of code. Let's add our function to appwrite console (see steps in above example) and test it out.


Here's what it prints -
{"isGlobal":false,"newConfirmed":14623,"totalConfirmed":34108996,"newDeaths":197,"totalDeaths":452651,"newRecovered":0,"totalRecovered":0}
Alright! In this article we learned the basics of Appwrite Function Service. In the next post, I'll show you how to connect an Appwrite Function to an android application. Stay tuned for that.