How to Manage Multiple Appwrite Functions in a Single Repository

Tosin Moronfolu - Oct 27 '23 - - Dev Community

In recent years, serverless computing has revolutionized the developer landscape, gaining widespread adoption for its benefits. Within this serverless paradigm, developers utilize serverless functions to execute tasks while reducing server costs and ensuring the scalability of their applications.

Appwrite, an open-source backend-as-a-service (BaaS) provider, has embraced this serverless revolution to empower developers further. With Appwrite's robust platform, developers can harness the full potential of serverless functions to create highly responsive and cost-effective web applications.

This article explores how to develop serverless functions on Appwrite and manage functions in a single GitHub repository. We'll build a Nuxt.js app that showcases how to create, use, and manage serverless functions with Appwrite.

Prerequisites

To follow along with this article, you’ll need the following:

  1. A basic understanding of JavaScript and Nuxt.js
  2. Node.js installed on your machine and a basic understanding of Node.js
  3. An Appwrite Cloud account (create one here)

Repository

All the code used in this article can be found in this repository:

https://github.com/folucode/quote-todo-app

Function setup

In this article, we’ll demonstrate building a simple to-do suggestion app: an app that suggests random things to do and also gives us motivational quotes to help us get motivated to complete the task. We’ll create two functions, one to suggest a random task and the other to give us a motivational quote. To achieve this, we’ll use Nuxt.js for the frontend of the app.

Creating the functions

First, let’s log in to our Appwrite cloud account and create a new project by clicking Create project and naming it Task-Suggestion.


Appwrite projects dashboard

Next, on the sidebar, click on Functions and then Create function.

appwrite dashboard

Since we’ll be managing our functions in a GitHub repository, click on the GitHub button, choose GitHub account, and then give it access to all repositories by selecting All repositories. Then, click on Install & Authorize.

After this, we‘ll be redirected to Appwrite for a dropdown list of our GitHub repositories. For this project, we will be using the Node.js runtime for our function.

Click on Node.js in the Quickstart section to set up our function and also create a GitHub repository to manage it. Here are the specifics:

  • Configuration: Name the function Get Quote and choose any Node.js runtimes from the dropdown list. Click on Next.
  • Variables: We won't use environment variables in our function, so leave it as it is and click Next.
  • Connect: Select the option to create a new repository and click Next.
  • Repository: Give the repository a name, make it private or public, and then click Next. Since we connected our GitHub account earlier, this new repository will be created in the GitHub account to which we connected the project.
  • Branch: Select the main branch, type functions/get_quote as the root directory of the function, and then click Create. Putting each function in its folder inside a central directory named functions is standard practice. This facilitates easy management of each function.

After this, our function is successfully created and deployed. It will also be in a repository on our GitHub profile.

Appwrite function deployment dashboard

Following the same steps, let’s create a new Get Task function. In the Connect section, select Add to existing repository. Next, select the previous repository we created for the Get Quote function. In the Branch section, type functions/get_task as the root directory and then click Create.

Appwrite will deploy the new function, and it will appear in the same repository. The GitHub repository should now look like this:

GitHub functions repository

and our Appwrite dashboard should look like this:

Appwrite functions dashboard

Writing the function logic

Now that we've created our functions, deployed them, and connected them to GitHub, we have to write the logic for our functions.

We first need to clone the project from GitHub using the following command:

git clone <repo-git-url>
Enter fullscreen mode Exit fullscreen mode

Open the project in a code editor and navigate to functions > get_quote > src > main.js. We’ll see some boilerplate code that was put in there by Appwrite. Replace the boilerplate code with the code below:

    export default async ({ req, res, log, error }) => {
      try {
        const motivationalQuotes = [
          {
            quote: "Success is not in what you have, but who you are.",
            author: "Bo Bennett"
          },
          {
            quote: "Your time is limited, don't waste it living someone else's life.",
            author: "Steve Jobs"
          },
          {
            quote: "The only person you are destined to become is the person you decide to be.",
            author: "Ralph Waldo Emerson"
          },
          {
            quote: "The best revenge is massive success.",
            author: "Frank Sinatra"
          },
          {
            quote: "Don't be afraid to give up the good to go for the great.",
            author: "John D. Rockefeller"
          }
        ];

        const randomIndex = Math.floor(Math.random() * motivationalQuotes.length);
        const randomQuote = motivationalQuotes[randomIndex];

        return res.json({
          statusCode: 200,
          body: JSON.stringify({ quote: randomQuote.quote, author: randomQuote.author }),
        });

      } catch (error) {
        return res.json({
          statusCode: 200,
          body: JSON.stringify({ error: 'An error occurred while fetching the quote.' }),
        });
      }
    };
Enter fullscreen mode Exit fullscreen mode

In this function, we define an array of motivational quotes, each being an object with a quote and an author. We then select a random quote and author and return a JSON response containing the selected quote and author. In case of any errors during this process, we return a JSON response indicating an error occurred.

Next, navigate to functions > get_task > src > main.js and replace the boilerplate code with this:

    export default async ({ req, res, log, error }) => {
      try {
        const tasks = [
          "Buy groceries",
          "Go for a run",
          "Read a book",
          "Call a friend",
          "Write a blog post",
          "Clean the house",
          "Cook dinner",
          "Watch a movie",
          "Take a walk in the park",
          "Plan a vacation",
          "Organize your closet",
          "Listen to music",
          "Practice mindfulness",
          "Try a new recipe",
          "Write in my journal",
        ];

        const randomTaskIndex = Math.floor(Math.random() * tasks.length);
        const randomTask = tasks[randomTodoIndex];

        return res.json({
          statusCode: 200,
          body: JSON.stringify({ task: randomTask }),
        });
      } catch (error) {
        return res.json({
          statusCode: 200,
          body: JSON.stringify({ error: 'An error occurred while fetching task.' }),
        });
      }
    };
Enter fullscreen mode Exit fullscreen mode

In the function above, we define an array of tasks, then select a random task and return a JSON response containing the chosen task. In case of any errors during this process, we return a JSON response indicating that an error occurred.

After doing this, push the changes to GitHub, and our functions in Appwrite will automatically redeploy with the new changes applied.

Frontend setup

We're using Nuxt.js for our frontend to implement the functions. To install Node, go to the Node.js website and follow the instructions to install the software compatible with our operating system.

Then, verify the installation using:

node -v
Enter fullscreen mode Exit fullscreen mode

To create the Nuxt.js app, run the command below; it will automatically set up a boilerplate Nuxt.js app:

npx nuxi@latest init <project-name>
Enter fullscreen mode Exit fullscreen mode

Next, change the directory to the directory of the app we just created:

cd <project-name>
Enter fullscreen mode Exit fullscreen mode

Now run npm run dev or yarn dev to start the development server on http://localhost:3000.

Building the functionality

Open the app.vue file and replace the boilerplate code with the code below:

    <template>
      <div class="parent">
        <div class="quote">
          <blockquote>{{ quote }}</blockquote>
          <p>- {{ author }} - </p>
        </div>
        <div class="todo">
          <p>- {{ task }} - </p>
        </div>
        <div class="get-quote">
          <button @click="getQuote">Get Quote</button>
        </div>
        <div class="get-todo">
          <button @click="getTask">Get Todo</button>
        </div>
      </div>
    </template>

    <style>
    .parent {
      display: grid;
      grid-template-columns: repeat(2, 1fr);
      grid-template-rows: repeat(2, 1fr);
      grid-column-gap: 0px;
      grid-row-gap: 0px;
      justify-content: center;
      align-items: center;
      place-items: center;
      width: 400px;
      margin-top: 150px;
      margin-left: 35%;
    }
    .quote {
      grid-area: 1 / 1 / 2 / 2;
    }
    .todo {
      grid-area: 1 / 2 / 2 / 3;
    }
    .get-quote {
      grid-area: 2 / 1 / 3 / 2;
    }
    .get-todo {
      grid-area: 2 / 2 / 3 / 3;
    }
    .quote {
      width: 250px;
      border-radius: 5px;
      padding: 5px;
      text-align: center;
      background-color: rgb(96, 206, 206);
      margin: 10px;
    }
    .todo {
      width: 250px;
      border-radius: 5px;
      padding: 5px;
      text-align: center;
      background-color: rgb(225, 73, 73);
    }
    blockquote {
      font-weight: bolder;
      font-size: larger;
      color: blanchedalmond;
      font-style: italic;
    }
    p {
      font-size: larger;
    }
    button {
      width: 150px;
      padding: 10px;
      background-color: brown;
      color: white;
      border: 2px solid white;
      border-radius: 5px;
    }
    button:hover {
      cursor: pointer;
    }
    </style>
Enter fullscreen mode Exit fullscreen mode

In the code above, we set up the UI for the app, define its positioning, and style it. Now, we need to implement the getQuote and getTask functions.

After the style tag, add the code below:

    <script>
    import { Client, Functions } from 'appwrite';

    const client = new Client()
      .setEndpoint('https://cloud.appwrite.io/v1')
      .setProject('[PROJECT-ID]');

    const functions = new Functions(client);

    export default {
      methods: {
        async getQuote() {
          try {
            const response = await functions.createExecution(
              '[FUNCTION-ID]',
              JSON.stringify({}),
              false,
              '/',
              'GET',
              {}
            );
            const result = JSON.parse(response.responseBody);
            const body = JSON.parse(result.body);
            this.quote = body.quote;
            this.author = body.author;
          } catch (error) {
            console.error('Error fetching the quote:', error);
          }
        },
        async getTask() {
          try {
            const response = await functions.createExecution(
              '[FUNCTION-ID]',
              JSON.stringify({}),
              false,
              '/',
              'GET',
              {}
            );
            const result = JSON.parse(response.responseBody);
            const body = JSON.parse(result.body);
            this.task = body.task;
          } catch (error) {
            console.error('Error fetching the quote:', error);
          }
        },
      },
      data() {
        return {
          quote: '',
          author: '',
          task: '',
        };
      },
    };
    </script>
Enter fullscreen mode Exit fullscreen mode

In the code above, we:

  1. Import the appwrite library to create a client and interact with Appwrite functions.
  2. Initialize the Appwrite client with the endpoint and our project ID. We get the project ID from our Appwrite dashboard by clicking on the Overview tab.
  3. Define the two methods, getQuote and getTask, which are used to call specific Appwrite functions.
    • getQuote sends a request to the Get Quote Appwrite function using its function ID and expects a response containing a quote and author. It then updates the Nuxt component's data properties quote and author with the retrieved values. We get the function ID from our Appwrite dashboard by clicking the Functions tab.
    • getTask sends a request to the Get Task Appwrite function using its function ID and expects a response containing a Task item. It updates the Nuxt component's task data property with the retrieved value. We get the function ID from our Appwrite dashboard by clicking the Functions tab.
  4. Initialize the Nuxt component's data properties with empty values, including quote, author, and task.

When we run our app, it should work like so:

https://www.loom.com/share/27fe40dc23574b198d9e5ad9cbb8716a?sid=cf39da90-9d6d-419e-98d0-7f640c929890

Conclusion

In this article, we've learned how to create and manage Appwrite functions all in one place on GitHub. We made two functions that perform specific tasks, helping us understand how to use Appwrite functions in our projects. We also learned that Appwrite deploys our functions automatically whenever we make changes, making our work smooth, and this simplifies the development process and ensures that our applications remain seamlessly up-to-date.

We also saw how we can have our functions in the same repository as our app. This helped to easily manage and sync all changes all in one single repository.

Resources

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .