Bidding farewell to Appwrite's Tasks Service 👋
Tags: javascript, programming, tutorial, opensource
Published: 2022-02-09 12:42:37

Appwrite's **Tasks Service** was a great way to send HTTP requests to remote URLs at specific intervals based on a CRON schedule. This allowed external services to react to events that take place within Appwrite and perform actions based on the request.

We deprecated Tasks in Appwrite 0.12  because Appwrite's Cloud Functions can now serve the same purposes as Tasks and can give you even more control than Tasks ever could! With Functions, you will be able to respond to events from Appwrite directly and easily recreate the Tasks functionality!! 

> **🤔 What is Appwrite?** 
[Appwrite](https://appwrite.io/) is an open source backend-as-a-service that abstracts all the complexity involved in building a modern application by providing you with a set of REST APIs for your core backend needs. Appwrite handles user authentication and authorization, databases, file storage, cloud functions, webhooks, and much more! If anything is missing, you can extend Appwrite using your favorite backend language.

The removal of the Tasks Service is a breaking change, and you may need to account for this before you migrate from 0.11 to prevent the functionality of your application from being broken. In this article, we'll see how to migrate your existing Tasks to Functions. 

## 👷‍♂️ Recreating the Tasks functionality using Functions

We will be using NodeJS for this example because it's quick and easy to write. I'll be making sure to explain what the code is doing so you can follow along and use this base even if you don't know JavaScript!

First, you want to create a new Function within Appwrite. Navigate to the Functions Service in the Appwrite dashboard, and then click `New Function`. Within the following popup, make sure to set the runtime to "Node 16.0" and give it a name of your choosing.

Next, create a folder somewhere on your computer to start writing code. Don't worry, you won't be writing too much code. By creating a folder to store everything, you make it easier to package it later. Now, create a new file called `main.js`. This file is where we will be holding all of our code, so let's paste the following code into the file (Don't worry, I'll break it down and explain it in a moment 🙂)

```javascript
// Configure this section
const reqURL =  new URL(process.env["REQUEST_URL"]);
const method = 'GET';
const AuthCredentials = {
    username: process.env["USERNAME"],
    password: process.env["PASSWORD"]
}
const headers = { 
// You can also add additional custom headers here.
    'User-Agent': 'Appwrite-Server',
    'X-Appwrite-Task-UID': process.env['APPWRITE_FUNCTION_ID'],
    'X-Appwrite-Task-Name': process.env['APPWRITE_FUNCTION_NAME']
};
// End of configuration

const client = reqURL.protocol == 'https' ? require('https') : require('http');

if (AuthCredentials.username && AuthCredentials.password) {
    headers['Authorization'] = 'Basic ' + Buffer.from(`${AuthCredentials.username}:${AuthCredentials.password}`).toString('base64');
}

const req = client.request({
    hostname: reqURL.hostname,
    port: reqURL.port,
    path: reqURL.pathname,
    method: method,
    headers: headers
}, (res) => {
    if (!(res.statusCode >= 200 && res.statusCode < 300)) {
        throw new Error('Request failed with status code: ' + res.statusCode);
    }
})

req.end();
```

At its core, it's a function that makes a simple HTTP request. It may look a little complicated because I have opted to use no dependencies, so it is effortless to package. Let me break it all down:

```javascript
const reqURL =  new URL(process.env["REQUEST_URL"]);
const method = 'GET';
const AuthCredentials = {
    username: process.env["USERNAME"],
    password: process.env["PASSWORD"]
}
const headers = { // You can add custom headers here.
    'User-Agent': 'Appwrite-Server',
    'X-Appwrite-Task-UID': process.env['APPWRITE_FUNCTION_ID'],
    'X-Appwrite-Task-Name': process.env['APPWRITE_FUNCTION_NAME']
};
```
The section above is the one you will be interested in the most. It holds all the configuration for this function and is the equivalent to the (now deprecated)Task dialogue from the Tasks Service. The only difference is that it's in code instead of the UI.

`reqURL`: This is the URL that we'll be making the request to. Make sure to specify the `https://` or `http://` protocol at the beginning. You'll see why later!

`method`: The HTTP method to use when making the request.

`authCredentials`: These will be credentials that will be used when making the request. They are encoded using the HTTP `Basic Authentication` method. You can leave `username` and `password` empty, if your endpoint does not require authentication.

`headers`: These are the headers that will be used when making the request. A few things of note here: 

- `User-Agent` will always be 'Appwrite-Server', just like in the old Tasks service.
- `X-Appwrite-Task-UID` is now the Function's ID
- `X-Appwrite-Task-Name` is the Function's Name
Feel free to add additional headers here if you like.

Now the rest is all the inner workings of this function. If you don't care about this, you can skip this section.

### 👨‍💻 Breakdown of the Code

```javascript
const client = reqURL.protocol == 'https' ? require('https') : require('http');
```
I was referencing this when I stated that you should remember to set protocol when defining your URL. This is because NodeJS uses different modules to make an HTTP or HTTPS request. This section of code simply automates the selection of the correct module.

```javascript
if (AuthCredentials.username && AuthCredentials.password) {
    headers['Authorization'] = 'Basic ' + Buffer.from(`${AuthCredentials.username}:${AuthCredentials.password}`).toString('base64');
}
```
This section computes the `Basic Authentication` header if it's required and sets it.

```javascript
const req = client.request({
    hostname: reqURL.hostname,
    port: reqURL.port,
    path: reqURL.pathname,
    method: method,
    headers: headers
}, (res) => {
    if (!(res.statusCode >= 200 && res.statusCode < 300)) {
        throw new Error('Request failed with status code: ' + res.statusCode);
    }
})
```
This final section makes the request, using all the previously declared variables with an arrow function to check if the status code was successful and throw an error if it isn't.

### 📦 Packaging the function
Packaging and using the function is simple! There are multiple ways to package an Appwrite function, and I'll go through the two most common ones.

#### 🚀 Appwrite CLI
The Appwrite CLI is a powerful tool for interacting and developing with Appwrite. We highly recommend it as it makes many tasks to do with Appwrite a breeze, including packaging an Appwrite function! You can read more about the Appwrite CLI and how to install it in our [docs](https://appwrite.io/docs/command-line).

Open your terminal and run the following command on Linux or macOS:

```sh
appwrite functions createTag \
    --functionId=YourFunctionID \
    --command='node main.js' \
    --code='/path/to/folder/with/function'
```
or on Powershell:

```sh
appwrite functions createTag `
    --functionId=YourFunctionID `
    --command='node main.js' `
    --code='/path/to/folder/with/function'
```
Make sure to replace `YourFunctionID` with the ID of the function you created earlier and replace `/path/to/folder/with/function` with the path to the folder you created to contain the `main.js` file.

Then go back to Appwrite and activate the tag you created using the command above.

Finally, you can navigate to the **Settings** tab of your function and set the CRON Schedule back to the one it was. It follows the same rules as the Tasks Service's CRON Schedule. You can also configure all the environment variables like the `username`, `password` and the `endpoint` in here. 

Once you're happy with the configuration don't forget to click the **Update** button at the bottom. 

#### 💻 Manual Packaging
You can also manually package the code into a tarball. Open up a terminal and navigate to the folder where you created your `main.js` file earlier. Then run the following command:
```
tar -cf code.tar.gz main.js
```
This command should create a `code.tar.gz` file within the directory where your code is.

Next, you want to navigate to your Appwrite console, return to the **Functions** service, and click on the function you created earlier. Within the overview tag of the function, click the **Deploy Tag** button, then click on **Manual** inside this dialogue, and set the command to:
```
node main.js
```
Finally, set the **Gzipped Code (tar.gz file)** field to the `code.tar.gz` file you created earlier, click on the **Create** button, and make sure you click the **Activate** button next to the tag you just created.

Finally, you can navigate to the **Settings** tab of your function and set the CRON Schedule back to the one it was. It follows the same rules as the Tasks Service's CRON Schedule. You can also configure all the environment variables like the `username`, `password` and the `endpoint` in here. 

Once you're happy with the configuration don't forget to click the **Update** button at the bottom. 

### 🎉 Congratulations!
You just successfully migrated your Task over to Functions! Wasn't that easy? Not only that, but you've levelled up the possibilities of your task 💪 Now you can call the URL when an Appwrite event happens, giving you even greater control of what your task can do!

You can use the following resources to learn more and get help
- 🚀 [Getting Started Tutorial](https://appwrite.io/docs/getting-started-for-server)
- 🚀 [Appwrite Github](https://github.com/appwrite)
- 📜 [Appwrite Docs](https://appwrite.io/docs)
- 💬 [Discord Community](https://appwrite.io/discord)