A comprehensive guide to the new syntax in Appwrite Functions 1.4

Odewole Babatunde Samson - Nov 1 '23 - - Dev Community

In the fast-paced development world, imagine having the power to effortlessly create, deploy, and manage serverless functions within your app, all while streamlining your development process.

Technology overview

Appwrite functions allow you to run backend code automatically in response to events triggered by Appwrite or by executing the code on a predefined schedule. Your code is securely stored on your Appwrite instance and is executed in an isolated environment.

When users of your application create an account or log in, you can define code in Appwrite functions to run and execute based on system events. Appwrite functions support multiple runtimes (like Node.js, Python, and PHP) and allow you to schedule the functions in your code to run at specific time intervals with your specified runtime.

Additionally, you can seamlessly create and deploy your functions by running commands in your terminal with the Appwrite CLI (which enables you to work with the Appwrite server).

Appwrite Cloud is an extension of the open-source version of Appwrite. It comes with all the services included in the open-source version but now boasts a fully managed service and minimum friction.

Project overview

This article takes a closer look at the magic of Appwrite Functions 1.4, demonstrating how to migrate your Appwrite version 1.3 functions to the latest version and take advantage of its new features and syntax changes in a Next.js application.

GitHub

Check out the complete source code here.

Prerequisites

To follow along with this tutorial, you should have a working knowledge of the following:

  • Basic understanding of JavaScript and Next.js
  • Installation of the Appwrite CLI
  • An Appwrite Cloud account; you can sign up here

What’s new in Appwrite Functions 1.4

In Appwrite 1.4, Functions are more accessible to learn, faster to debug, painless to maintain, and more flexible than ever. The Functions workflow has been reimagined, guided by the community’s help and feedback.

Below are some notable changes in the Appwrite Functions 1.4:

  • First, template codes have been introduced to Appwrite 1.4 Functions. You can add Functions engineered by the Appwrite team and community to your Appwrite project. You can start leveraging the powers of Appwrite Functions and the community without writing a single line of code.
  • Also, the Functions syntax has been revised based on feedback. The request data will now contain all the expected components of a typical HTTP request.
  • A new dedicated log method has been included, so you’ll never have disappearing logs on a function you’re trying to debug again.
  • Finally, in 1.4, you can deploy Appwrite Functions directly from Git. Appwrite Functions will now fit into your existing workflow alongside the rest of your code.

In this tutorial, you’ll start with setting up a frontend app for a demo that eventually illustrates the difference and migration from 1.3 to 1.4.

Frontend setup

The frontend is a basic Next.js form page that handles form submissions and can be used to store a user's information in a collection. The form is submitted to the function using the POST method, and the form data will be sent as a URL-encoded string in the request body.

To set up your application, you'll clone this repository using the command below:



git clone https://github.com/Tundesamson26/appwrite-functions-guide.git


Enter fullscreen mode Exit fullscreen mode

Then, run the command below in the project directory:



$ cd appwrite-functions-guide
$ npm install


Enter fullscreen mode Exit fullscreen mode

This repository contains all the initial setups you'll need for your app, helping you focus on your app's main functionality.

Then, run the development server using the command below:



npm run dev


Enter fullscreen mode Exit fullscreen mode

Once the development server runs, you can view it on your browser at http://localhost:3000.

Creating an Appwrite Cloud project

To get started, log into Appwrite Cloud, click the Create project button, input appwrite-functions-guide as the name, and then click Create.

appwrite-create-project

The project dashboard will appear on the console. Next, copy the Project ID; you’ll use this to set up your Next.js application.

appwrite-project-tab

You will also need to get an API Key, which will be useful in the next step. To create an API Key, click on the project overview and then click on API Key with the name functions_guide.

appwrite-api-key

Next is to choose which permission scopes to grant your application. In this case, choose Functions and Databases and click on Create.

appwrite-api-key-scope

Creating functions in Appwrite 1.3

To create a function in Appwrite 1.3 version and below, you will start by logging onto Appwrite's console using the Appwrite CLI, like so:



appwrite login


Enter fullscreen mode Exit fullscreen mode

Running the command above requires you to use your Appwrite credentials
(i.e., your Appwrite email and password) in the terminal.

After logging into the application, you will initialize and link the Next.js application with the previously created Appwrite project. To do so, you will run the command below:



appwrite init project


Enter fullscreen mode Exit fullscreen mode

Running the command above will display a list of options from which you will pick those appropriate for your project.

Next, create a function in your Next.js application by running the command below:



appwrite init function


Enter fullscreen mode Exit fullscreen mode

Upon the successful execution of the above command, you will see a new folder named functions/<function-name>/src/index.js in your Next.js application.

Appwrite Functions 1.3 logic

In order for your Appwrite Functions 1.3 to handle the form submissions, navigate to src/index.js and paste the code below:



const sdk = require("node-appwrite");
const querystring = require ("node:querystring");

module.exports = async  (req, res) => {
  if (req.method === "GET") {
    return res.send(html, 200, { "content-type": "text/html" });
  }
  if ( req.method === "POST" && req.variables.headers["content-type"] === "application/x-www-form-urlencoded") {
    const formData = querystring.parse(req.body);
    const message = {
      name: formData.name,
      email: formData.email,
      date: formData.date,
      time: formData.time,
      content: formData.content,
    };
    const client = new sdk.Client();
    client
      .setEndpoint(req.variables['https://cloud.appwrite.io/v1'])
      .setProject(req.variables['APPWRITE_FUNCTION_PROJECT_ID'])
      .setKey(req.variables['APPWRITE_FUNCTION_API_KEY'])
      .setSelfSigned(true);
    const databases = new sdk.Databases(client);
    const document = await databases.createDocument(
      "650efb16ae5ebb92185a",
      "650efb593f6c9f97c09c",
      ID.unique(),
      message
    );
    return res.send("Message sent");
  }
  return res.send("Not found", 404);
};


Enter fullscreen mode Exit fullscreen mode

The code block above achieved the following:

  • Imported the node-appwrite SDK from Appwrite.
  • Exported an asynchronous function req and res using the Appwrite serverless function framework
  • GET Request Handling:
    • If the incoming request method is GET, then the function sends an HTML response (text/html) with the html content. The response code is set to 200, indicating a successful response.
  • POST Request Handling:
    • If the incoming request method is POST and the content type in the request headers is 'application/x-www-form-urlencoded', then the function proceeds to handle the POST request.
    • It parses the request body (which is assumed to be in URL-encoded format) using querystring.parse to extract form data.
    • It creates a message object containing properties like name, email, date, time, and content by extracting data from the parsed form data.
    • It creates an instance of the Appwrite client using the sdk.Client().
    • It configures the client with an Endpoint, projectID, and API key based on values from the req.variables object.
    • It creates a new instance of the **sdk.Databases** using the client.
    • Finally, it uses the createDocument() method to create a new document in the Appwrite database. The document data is based on the message object created earlier.
  • After successfully creating the document, the function returns an HTTP response with the text "Message sent."
  • If the request method is neither GET nor POST or the content type is not 'application/x-www-form-urlencoded', then the function returns an HTTP response with the text "Not Found" and a status code of 404.

Deploying the Appwrite Function 1.3
To deploy the Appwrite function, run the command below:



appwrite deploy function


Enter fullscreen mode Exit fullscreen mode

Executing the above command will require you to pick the appropriate function you want to deploy. Upon successful deployment, you will get a message like the one below in your terminal.



 Success Deployed <function-name> ( functionID)


Enter fullscreen mode Exit fullscreen mode

Migrating to Appwrite Functions 1.4

On the left side of the Appwrite Cloud dashboard, click on the Templates tab to create a starter function.

appwrite-functions-template

Clicking on the Create function button will take you to the tab where you can start your configuration, like this.

appwrite-function-config

The next thing you should do is create the variable, and here is where you will paste the API KEY you created earlier.

appwrite-function-variable

Then, Connect to your repository by choosing either “Create a new repository” or “Add to existing repository” (in this case, you are connecting with the existing Next.js repository).

appwrite-function-github-connect

Next, choose the GitHub repository to trigger the Appwrite function.

appwrite-function-github-repo

Now, you should complete your configuration by configuring the Git production branch and clicking on Create.

appwrite-function-github-branch

NOTE: The CLI deployment is still available, but Git deployment is preferable.

After completing the configurations, you should have an active Function deployed like this.

appwrite-functions-deployment-tab

Completing the above configuration will create a new Appwrite function src/main.js file in your repository.

Next, navigate to the Domains tab from the Functions page and copy the verified generated URL.

appwrite-functions-domain-tab

Appwrite Functions 1.4 logic

Following the exact function implementation from the Function version 1.3 above—but this time in Appwrite 1.4—navigate to src/main.js and paste the code below:



import { Client, Databases, ID } from "node-appwrite";
import querystring from "node:querystring";

export default async ({ req, res, log, error }) => {
  // You can log messages to the console
  log("Hello, Logs!");
  // If something goes wrong, log an error
  error("Hello, Errors!");
  if (req.method === "GET") {
    return res.send(html, 200, { "content-type": "text/html" });
  }
  if (
    req.method === "POST" &&
    req.headers["content-type"] === "application/x-www-form-urlencoded"
  ) {
    const formData = querystring.parse(req.body);
    const message = {
      name: formData.name,
      email: formData.email,
      date: formData.date,
      time: formData.time,
      content: formData.content,
    };
    const client = new Client();
    client
      .setEndpoint("https://cloud.appwrite.io/v1")
      .setProject(process.env.APPWRITE_FUNCTION_PROJECT_ID)
      .setKey(process.env.APPWRITE_API_KEY);
    const databases = new Databases(client);
    const document = await databases.createDocument(
      "650efb16ae5ebb92185a",
      "650efb593f6c9f97c09c",
      ID.unique(),
      message
    );
    return res.send("Message sent");
  }
  return res.send("Not found", 404);
};


Enter fullscreen mode Exit fullscreen mode

These are what changed in the above code compared to version 1.3:

  • The parameter passed into functions has changed. req and res have been replaced by the context object.
  • To improve privacy and logging reliability, new context.log() and context.error() functions were provided.
  • The old way of req.variables has been deprecated; variables are now passed into each function as environment variables.
  • Some information, like triggers and events, are now passed in as headers instead of variables.
  • The req object has been updated to use terminology consistent with typical HTTP concepts, which include familiar concepts like headers, body, HTTP methods, and others.
  • By default, the request and response information is not logged. This is to protect user information by default.
  • A response such as context.res.send("") is returned. This prevents confusing errors when functions are terminated prematurely before a response is sent.

By now, the application should look like this:

https://www.loom.com/share/570cb616f33241e5b9a334057bcd4471?sid=0d91fbb4-e1c0-43dd-aa2b-3858b1dc9bc3

The Appwrite Cloud Functions Execution completed a new Function like this:

appwrite-function-executions

And the Appwrite Cloud Databases updated the database collection like this:

appwrite-database-tab

Conclusion

This article discussed how to migrate to Appwrite Functions 1.4 and implement them in a Next.js application. This article also provided a detailed and easy guide on creating an Appwrite Cloud project and utilizing the amazing features and the revised syntax of the Appwrite Functions 1.4.

Resources

Here are some additional resources that might be helpful:

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