Send Welcome Emails using Appwrite's Java Cloud Functions!
Tags: opensource, cloud, java, serverless
Published: 2021-07-12 09:48:25

**Appwrite 0.9** adds support for both **Java and Kotlin runtimes** in Cloud Functions, growing the list of supported runtimes to **over 15(!)** 

This tutorial will walk you through the process of creating and uploading a Java Cloud Function. In particular, we'll teach you how to use Cloud Functions to send customized welcome emails to your users when they sign up! This example also highlights how Appwrite can integrate seamlessly with 3rd party APIs and coexist with your existing stack. Without further ado, let's dive right in!

## 📝 Prerequisites

At this stage, we assume that you already have an Appwrite instance up and running. If you do not have Appwrite setup yet, you can follow the super easy installation **step** over at [appwrite.io](https://appwrite.io/docs/installation). It's not a typo. There really is only 1 step! 

You also need to setup an [Mailgun](https://www.mailgun.com/) or [SendGrid](https://sendgrid.com/) account to be able to send emails. 

## 📽️ Create your project 

Create a new Maven project using IntelliJ ( or any other IDE you're comfortable with ). Set the name and artifact ID accordingly and click **Finish**. 
![create_project](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/m1h20wfk6j7gmwbof5nl.png)
 

Once the project is created, we'll add our dependencies. For this example, we will need just one dependency to make our HTTP requests.

* **Unirest Java** (com.mashape.unirest:unirest-java:1.4.9)

Add the following lines to the `<dependencies>` section of your `pom.xml` file. 

```xml
<dependencies>
    <dependency>
        <groupId>com.mashape.unirest</groupId>
        <artifactId>unirest-java</artifactId>
        <version>1.4.9</version>
    </dependency>
</dependencies>
```

Update your Maven dependencies from the UI or using the shortcut `Ctrl + Shift + O` 

![refresh_maven](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cgq0q8hwtgewib2kjbz7.png)
 

## 👩‍💻 Write some code 

Now that the dependencies are fetched, it's time to write some code.

Create a new Java class under `src/main/java` and name it `WelcomeEmail.java`.

Next, create a `main` function that fetches all the environment variables and parses the JSON string stored in the environment variable `APPWRITE_FUNCTION_EVENT_DATA`. 

`APPWRITE_FUNCTION_EVENT_DATA` is a special environment variable that is automatically passed to your Cloud Function when it is triggered by an event. In our case, the Cloud Function is triggered by a [`users.create`](https://appwrite.io/docs/server/users?sdk=nodejs#usersCreate) and [`account.create`](https://appwrite.io/docs/client/account?sdk=web#accountCreate) event, so the payload in `APPWRITE_FUNCTION_EVENT_DATA` contains a [User Object](https://appwrite.io/docs/models/user).

We are interested in the name and email ID of the user so we can send a personalised email. We will extract those values here. 

```java
import com.mashape.unirest.http.exceptions.UnirestException;
import org.json.JSONObject;

public class WelcomeEmail {

    private static String YOUR_DOMAIN_NAME;
    private static String API_KEY;

    public static void main(String[] args) throws UnirestException {
        YOUR_DOMAIN_NAME = System.getenv("MAILGUN_DOMAIN");
        API_KEY = System.getenv("MAILGUN_API_KEY");
        String payload = System.getenv("APPWRITE_FUNCTION_EVENT_DATA");

        if (payload != null && !payload.isEmpty()) {
            try {
                // Parse the JSON string into a JSON Object
                JSONObject json = new JSONObject(payload);
                String name = json.getString("name");
                String email = json.getString("email");

            } catch (Exception e) {
                System.out.print("[ERROR] There was an error");
                System.out.println(e.getMessage());
            }
        } else {
            System.out.println("[INFO] APPWRITE_FUNCTION_EVENT_DATA is empty");
        }
    }
}

```

Don't worry about the other environment variables yet, we will set them up in the Dashboard in the coming sections.

Next, let's create a simple function that sends a POST request to the Mailgun API and sends out our email.

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

public static String sendSimpleMessage(String name, String email) throws UnirestException {

    String message = String.format("Hi %s!", name);
    
    HttpResponse<String> request = Unirest.post("https://api.mailgun.net/v3/" + YOUR_DOMAIN_NAME + "/messages")
            .basicAuth("api", API_KEY)
            .field("from", "Your Awesome App <hello@yourawesomeapp.com>")
            .field("to", email)
            .field("subject", "Welcome Onboard")
            .field("text", message)
            .asString();

    return request.getBody();
}
```

Finally, call this function from our main function.

```diff
public static void main(String[] args) throws UnirestException {
        YOUR_DOMAIN_NAME = System.getenv("MAILGUN_DOMAIN");
        API_KEY = System.getenv("MAILGUN_API_KEY");
        String payload = System.getenv("APPWRITE_FUNCTION_EVENT_DATA");

        if (payload != null && !payload.isEmpty()) {
            try {
                JSONObject json = new JSONObject(payload);
                String name = json.getString("name");
                String email = json.getString("email");
+               String response = sendSimpleMessage(name, email);
+               System.out.println(response);
            } catch (Exception e) {
                System.out.print("[ERROR] There was an error");
                System.out.println(e.getMessage());
            }

        } else {
            System.out.println("[INFO] APPWRITE_FUNCTION_EVENT_DATA is empty");
        }

    }
```

Now that we've written all the required code, we need to package our function as a `.jar`. Fortunately, this can be done really easily using IntelliJ so let's see how.

## ⚙️ Configure artifacts. 

In this step, we will create the `.jar` artifacts required to deploy our Cloud Function. Select `File > Project Structure > Artifacts` as seen in the screenshot.

![configure_artifacts](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/brrz1vzo2q3rh6knzvmm.png)

In the following dialog, select your main class ( `WelcomeEmail` ) as shown in the image.

![configure_artifacts_2](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lzfj7ffs62bw2zehmvag.png)
 

> Note: If you don't see the main class, try to follow the steps in this answer https://stackoverflow.com/questions/10654120/error-could-not-find-or-load-main-class-in-intellij-ide 

In the next dialog, click Apply and then OK.

Confirm that a new file was created at `src/main/java/META-INF/MANIFEST.MF` with the following contents.

```
Manifest-Version: 1.0
Main-Class: WelcomeEmail
```

Now build your artifacts using `Build > Build Artifacts > Select your artifact from the list > Build`. You will find the output of this step in `out/artifacts/welcome_email_jar/welcome-email.jar`

## 🧪 Test locally 

Great! Let's test if your function is working fine and doesn't have any compilation issues. Run the following command from the root directory of your Java project. Make sure you replace the values of the required environment variables with those of your own setup. 

* MAILGUN_API_KEY
* MAILGUN_DOMAIN 
* APPWRITE_FUNCTION_EVENT_DATA

Your **MAILGUN_DOMAIN** should look something like **sandboxee5d....b85.mailgun.org**. Also ensure that the email address is an [authorized recipient](https://help.mailgun.com/hc/en-us/articles/217531258-Authorized-Recipients).

```sh
docker run --rm --volume $(pwd):/usr/local/src:rw \                                   
    --env MAILGUN_API_KEY="YOUR_API_KEY" \
    --env MAILGUN_DOMAIN="sandboxee5d...b85.mailgun.org" \
    --env APPWRITE_FUNCTION_EVENT_DATA="{ \"name\" : \"Example\", \"email\": \"example@example.com\"}" \
    appwrite/runtime-for-java:11 \
    java -jar out/artifacts/welcome_email_jar/welcome-email.jar 
```

If everything goes well, you should see the following output

```json
{
  "id": "<20210625073914.1.D719405DDA651887@sandboxee5d...b85.mailgun.org>",
  "message": "Queued. Thank you."
}
```

## 🌩️ Create your Cloud Function

Head over to the Appwrite Dashboard and navigate to the **Functions** Tab on the sidebar and click on **Add Function** . Give your function a Name, select an appropriate Java runtime and click `Create`. Keep a note of your function ID as we need this to deploy our function using the CLI.

Next, head over to the **Settings** tab in your Cloud Function and add the required environment variables and select the events that should trigger this function. In our case this would be `account.create` and `users.create`.
![configure_functions](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/enewtpbyrdc5ewp5f839.png)


Don't forget to click **Update** to save your settings.

## ↗️ Deploying & Execute

We're now ready to deploy our function. This step can be done either with the Appwrite CLI or manually.

### 👀 Deploying Manually

Head over to the root directory of your Java project and run the following commands to create a `tarfile`.

```bash
$ cd out/artifacts

$  tar -zcvf code.tar.gz welcome_email_jar 

welcome_email_jar/
welcome_email_jar/welcome-email.jar

$  ls
code.tar.gz  welcome_email_jar
```

This will create a new archive called `code.tar.gz`.

With this created, head over to your `Appwrite Dashboard > Functions > Overview > Deploy Tag`. In the dialog that pops up, upload the `tarfile` we just created and use `java -jar welcome-email.jar` for the entry point command. 

Once your function is successfully uploaded you need to activate your tag by clicking the **Activate Button**. 

Great, in order to trigger our function, we need to create a user. Head over to the **Users** section on the sidebar and create a new user using an email ID that is present in the list of authorized recipients. If everything goes well, you should be able to see the execution logs under the `Logs` tab.
![logs](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cx3a6wv33ljklstozg9z.png)

### ⌨️ Deploying with the CLI 

If using the Appwrite CLI, run the following commands from the root directory of your Java project. Be sure to replace the IDs with your own values. You can get your function ID from your Appwrite Dashboard.

```sh
$ cd out/artifacts/

$ appwrite functions createTag --functionId=60d5839682e94 --command="java -jar welcome-email.jar" --code=welcome_email_jar

$id : 60d58cacb0748
functionId : 60d5839682e94
dateCreated : 1624607916
command : java -jar welcome-email.jar
size : 1808608

$  appwrite functions updateTag --functionId=60d5839682e94 --tag=60d58cacb0748

$id : 60d5839682e94
$permissions : 
name : Welcome Email Test
dateCreated : 1624605590
dateUpdated : 1624606138
status : disabled
runtime : java-11
tag : 60d58cacb0748
vars : 
events : 
schedule : 
scheduleNext : 
schedulePrevious : 0
timeout : 15
```

Your Cloud Function is now active and will be triggered whenever a new user is created. 

Great! You've successfully deployed and executed your first Java Cloud Function! This is just the tip of the iceberg, and the possibilities with Cloud Functions are endless! Stay tuned for more Cloud Function ideas from the Appwrite Team.

If you'd like to learn more about Appwrite or how Appwrite works under the hood, we've just curated all the resources for you during [30 Days of Appwrite](https://30days.appwrite.io/).

## ✨️ Credits 

Hope you enjoyed this article! You can find the complete code for this tutorial over at the [Cloud Functions Demo](https://github.com/appwrite/demos-for-functions) repository where we have a lot more examples for various runtimes. We love contributions and encourage you to take a look at our [open isuses](https://github.com/appwrite/appwrite/issues) and [ongoing RFCs](https://github.com/appwrite/rfc/pulls).

If you get stuck anywhere, feel free to reach out to us on our [friendly support channels](https://appwrite.io/discord) run by humans.  

Here are some handy links for more information:
- [Appwrite Contribution Guide](https://github.com/appwrite/appwrite/blob/master/CONTRIBUTING.md)
- [Appwrite Github](https://github.com/appwrite)
- [Appwrite Documentation](https://appwrite.io/docs)